diff --git a/app/(pages)/blog/[slug]/page.tsx b/app/(pages)/blog/[slug]/page.tsx index 5fc967b..2e47b61 100644 --- a/app/(pages)/blog/[slug]/page.tsx +++ b/app/(pages)/blog/[slug]/page.tsx @@ -63,7 +63,7 @@ export default function BlogArticle({ params }: any) { const isNewsletter = post?.title?.toLowerCase().includes("newsletter") || - post?.tags?.includes("newsletter") || + post?.tags?.some((tag) => tag.name.toLowerCase().includes("newsletter")) || post?.tldr?.toLowerCase()?.includes("newsletter") return ( @@ -142,12 +142,12 @@ export default function BlogArticle({ params }: any) {
{post?.tags?.map((tag) => ( - + ))} diff --git a/app/(pages)/blog/page.tsx b/app/(pages)/blog/page.tsx index 29da54a..dd935f8 100644 --- a/app/(pages)/blog/page.tsx +++ b/app/(pages)/blog/page.tsx @@ -10,21 +10,22 @@ import { } from "@tanstack/react-query" import ArticlesList from "@/components/blog/ArticlesList" import { Skeleton } from "@/components/skeleton" +import { getArticles } from "@/lib/blog" export const dynamic = "force-dynamic" export const revalidate = 60 // Revalidate every 60 seconds export const metadata: Metadata = { - title: "Blog", - description: "", + title: LABELS.BLOG_PAGE.TITLE, + description: LABELS.BLOG_PAGE.SUBTITLE, } interface BlogPageProps { searchParams?: { [key: string]: string | string[] | undefined } } -const LoadingSkeleton = () => { +export const BlogLoadingSkeleton = () => { return ( <>
@@ -67,19 +68,8 @@ const BlogPage = async ({ searchParams }: BlogPageProps) => { queryKey: ["articles", tag], queryFn: async () => { try { - const params = new URLSearchParams() - if (tag) params.append("tag", tag) - - const response = await fetch(`/api/articles?${params.toString()}`, { - next: { revalidate }, - }) - - if (!response.ok) { - throw new Error(`Failed to fetch articles: ${response.status}`) - } - - const data = await response.json() - return data.articles || [] + const articles = getArticles({ tag }) + return articles } catch (error) { console.error("Error fetching articles:", error) return [] @@ -104,9 +94,9 @@ const BlogPage = async ({ searchParams }: BlogPageProps) => {
- }> + }> - } /> + diff --git a/app/(pages)/blog/tags/[tag]/page.tsx b/app/(pages)/blog/tags/[tag]/page.tsx new file mode 100644 index 0000000..b5b3dc3 --- /dev/null +++ b/app/(pages)/blog/tags/[tag]/page.tsx @@ -0,0 +1,101 @@ +import { LABELS } from "@/app/labels" +import { ArticleListCard } from "@/components/blog/article-list-card" +import { AppContent } from "@/components/ui/app-content" +import { Label } from "@/components/ui/label" +import { getArticles, Article, getArticleTags } from "@/lib/blog" +import { interpolate } from "@/lib/utils" +import { + HydrationBoundary, + QueryClient, + dehydrate, +} from "@tanstack/react-query" +import { Suspense } from "react" +import { BlogLoadingSkeleton } from "../../page" +import Link from "next/link" +import { Icons } from "@/components/icons" +import { Metadata } from "next" + +interface BlogTagPageProps { + params: { tag: string } +} + +export async function generateMetadata({ + params, +}: BlogTagPageProps): Promise { + const { tag } = params + + return { + title: interpolate(LABELS.BLOG_TAGS_PAGE.TAG_TITLE, { tag }), + description: LABELS.BLOG_TAGS_PAGE.SUBTITLE, + } +} + +export const generateStaticParams = async () => { + const tags = await getArticleTags() + return tags.map((tag) => ({ tag })) +} + +const BlogTagPage = async ({ params }: BlogTagPageProps) => { + const { tag } = params + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ["get-articles-by-tag", tag], + queryFn: async () => { + try { + const articles = getArticles({ tag }) + return articles + } catch (error) { + console.error("Error fetching articles:", error) + return [] + } + }, + }) + + const articles = queryClient.getQueryData([ + "get-articles-by-tag", + tag, + ]) as Article[] + + return ( +
+
+ + + + + {LABELS.BLOG_TAGS_PAGE.BACK_TO_ARTICLES} + + + + +
+ + + }> + +
+ {articles?.map((article: Article) => ( + + ))} + {articles?.length === 0 && ( +

+ No articles found for tag "{tag}" +

+ )} +
+
+
+
+
+ ) +} + +export default BlogTagPage diff --git a/app/(pages)/blog/tags/page.tsx b/app/(pages)/blog/tags/page.tsx new file mode 100644 index 0000000..88947de --- /dev/null +++ b/app/(pages)/blog/tags/page.tsx @@ -0,0 +1,70 @@ +import { LABELS } from "@/app/labels" +import { Icons } from "@/components/icons" +import { AppContent } from "@/components/ui/app-content" +import { Label } from "@/components/ui/label" +import { getArticleTags } from "@/lib/blog" +import { HydrationBoundary, QueryClient } from "@tanstack/react-query" +import Link from "next/link" +import { Suspense } from "react" +import { Metadata } from "next" + +export const metadata: Metadata = { + title: LABELS.BLOG_TAGS_PAGE.TITLE, + description: LABELS.BLOG_TAGS_PAGE.SUBTITLE, +} + +const BlogTagsPage = async () => { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ["get-articles-tags"], + queryFn: async () => { + try { + const articles = getArticleTags() + return articles + } catch (error) { + console.error("Error fetching articles:", error) + return [] + } + }, + }) + + const tags = queryClient.getQueryData(["get-articles-tags"]) as string[] + + return ( +
+
+ + + + + {LABELS.BLOG_TAGS_PAGE.BACK_TO_ARTICLES} + + + + +
+ + + + + {tags?.map((tag) => ( + + {tag} + + ))} + + + +
+ ) +} + +export default BlogTagsPage diff --git a/app/labels.ts b/app/labels.ts index f9b544a..951c1ec 100644 --- a/app/labels.ts +++ b/app/labels.ts @@ -13,7 +13,7 @@ export const LABELS = { { TITLE: "01. Cryptography for people", DESCRIPTION: [ - 'Cryptography is everywhere: every time you connect to a secure site, log in with a password or unlock your phone, you\'re seeing cryptography in action. With "programmable" cryptography (like zero knowledge proofs, multi-party computation or homomorphic encryption) we can make verifiable claims about secret information without revealing the information itself.', + "Cryptography is everywhere: every time you connect to a secure site, log in with a password or unlock your phone, you're seeing cryptography in action. With 'programmable' cryptography (like zero knowledge proofs, multi-party computation or homomorphic encryption) we can make verifiable claims about secret information without revealing the information itself.", " This can be applied to identity management, collusion resistance, anonymous communication and so much more. We're building a library of dev tools, research papers, and prototypes that are open source and free for everyone to use. We hope our resources inspire people to innovate the technologies that their communities need.", ], }, @@ -48,6 +48,13 @@ export const LABELS = { SEE_MORE: "See more", READ_MORE: "Read more", }, + BLOG_TAGS_PAGE: { + TITLE: "Blog tags", + TAG_TITLE: "Blog posts tagged with: '{{tag}}'", + SUBTITLE: + "Read our latest articles and stay updated on the latest news in the world of cryptography.", + BACK_TO_ARTICLES: "Back to articles", + }, COMMON: { SITE_TITLE: "Privacy & Scaling Explorations", SITE_DESCRIPTION: diff --git a/articles/are-elliptic-curves-going-to-survive-the-quantum-apocalypse.md b/articles/are-elliptic-curves-going-to-survive-the-quantum-apocalypse.md index d3e2ed7..e3cbf83 100644 --- a/articles/are-elliptic-curves-going-to-survive-the-quantum-apocalypse.md +++ b/articles/are-elliptic-curves-going-to-survive-the-quantum-apocalypse.md @@ -6,8 +6,6 @@ tldr: "Quantum computers will shatter today’s elliptic-curve schemes (and the date: "2025-05-20" --- -# Are elliptic curves going to survive the quantum apocalypse? - Elliptic curves offer unmatched cryptographic power, especially with [pairings](https://en.wikipedia.org/wiki/Pairing-based_cryptography) that enable advanced protocols like identity-based encryption and short signatures. But quantum computers threaten this entire ecosystem by breaking the discrete logarithm problem—on which these schemes depend. However, elliptic curves might retain their place in cryptography after all—through entirely different assumptions. @@ -16,7 +14,6 @@ However, elliptic curves might retain their place in cryptography after all—th Elliptic curves are wonderful—no other area of mathematics combines cryptographic versatility with computational efficiency so effectively. Just think of pairings—you can do all kinds of things with pairings. You can construct identity-based encryption, build short signatures, enable non-interactive zero-knowledge proofs, or design functional encryption schemes. It's a beautiful intersection of theory and practice, where abstract concepts translate directly into cutting-edge protocols. - The fact that pairings can be defined on elliptic curves—which were already a highly attractive setting for cryptography even before pairings came into use—is, as one of the pioneers of pairing-based cryptography, Ben Lynn, [put it](https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf), a “happy coincidence”. Pairings are special bilinear maps defined on groups derived from elliptic curves or higher-dimensional abelian varieties. A typical pairing has the form: @@ -26,6 +23,7 @@ e: G_1 \times G_2 \to G_T $$ where $G_1$, $G_2$, and $G_T$ are groups of the same prime order $p$. There’s one crucial property of pairings, called bilinearity: + $$ e(aP, bQ) = e(P, Q)^{ab}. $$ @@ -33,6 +31,7 @@ $$ Pairings allow one to perform certain operations on hidden exponents without solving the discrete logarithm. Suppose you have: + $$ A = aP \in G_1 $$ @@ -42,16 +41,16 @@ B = bQ \in G_2 $$ You cannot recover $a$ or $b$ ([discrete logatihm](https://en.wikipedia.org/wiki/Discrete_logarithm) is hard), but using a bilinear pairing, you can compute: + $$ e(A, B) = e(aP, bQ) = e(P, Q)^{ab} $$ This means that although $a$ and $b$ are hidden, you can compute something that depends on their product—without knowing either value individually. - ## Will quantum computers kill the magic of elliptic curves? -So, is all this magic set to *die* with the advent of quantum computers? +So, is all this magic set to _die_ with the advent of quantum computers? Given two points $P$ and $R$ on an elliptic curve, a quantum computer can efficiently compute an integer $\ell$ such that $\ell P = R$, if such an $\ell$ exists. This task—known as the discrete logarithm problem—is believed to be hard for classical computers, and the security of virtually all elliptic curve cryptography depends on that hardness. @@ -61,7 +60,7 @@ Will pairings be lost as well? Unfortunately, yes. Pairing-based cryptography relies on the discrete logarithm problem being hard (in all three groups: $G_1$, $G_2$, and $G_T$). -So yes, my friend. *You should be sad that elliptic curve cryptography is going to be buried.* +So yes, my friend. _You should be sad that elliptic curve cryptography is going to be buried._ ## Unless... @@ -71,7 +70,6 @@ Isogeny-based cryptography is a post-quantum alternative that still relies on el ![genus-1](https://hackmd.io/_uploads/S1Chdpybee.png) - This means that, given two elliptic curves, computing an explicit isogeny between them is believed to be computationally hard—even for a quantum computer. Yeah, you might mention [Castryck-Decru attack](https://eprint.iacr.org/2022/975), but as I wrote [last time](https://pse.dev/en/blog/code-optimizations-in-the-landscape-of-post-quantum-cryptography), this applies only to certain special isogeny-based schemes. @@ -99,10 +97,13 @@ To evaluate the image of a point under the isogeny—that is, to determine where The theta model significantly accelerates isogeny-based cryptography in higher dimensions. But what’s even more intriguing is the possibility that abelian varieties—and the theta machinery—could one day enable [trilinear maps](https://hal.science/tel-03498268/document). And you know, trilinear maps are bilinear maps on steroids. A trilinear map is a function + $$ e : G_1 \times G_2 \times G_3 \to G_T $$ + where all groups have the same prime order $p$, satisfying the following property: + $$ e(aP, bQ, cR) = e(P, Q, R)^{abc} $$ diff --git a/articles/efficient-client-side-proving-for-zkid.md b/articles/efficient-client-side-proving-for-zkid.md index e9d5185..268c5b1 100644 --- a/articles/efficient-client-side-proving-for-zkid.md +++ b/articles/efficient-client-side-proving-for-zkid.md @@ -4,16 +4,22 @@ title: "Efficient Client-Side Proving for zkID" image: "/articles/efficient-client-side-proving-for-zkid/cover.webp" # Image used as cover, Keep in mind the image size, where possible use .webp format, possibly images less then 200/300kb tldr: "Binius and Ligero emerge as the top candidates for mobile SHA-256 ZK-proving under transparent, post-quantum constraints." #Short summary date: "2025-06-11" # Publication date in ISO format -tags: ["client-side proving", "zkp", "zero-knowledge proofs", "zkid", "post-quantum", "benchmarks"] # (Optional) Add relevant tags as an array of strings to categorize the article +tags: [ + "client-side proving", + "zkp", + "zero-knowledge proofs", + "zkid", + "post-quantum", + "benchmarks", + ] # (Optional) Add relevant tags as an array of strings to categorize the article projects: ["client-side-proving", "zk-id"] --- -# Efficient Client-Side Proving for zkID - **TL;DR: Binius and Ligero emerge as the top candidates for mobile SHA-256 ZK-proving under transparent, post-quantum constraints.** This work is helpful for wallet developers, standards authors, and ZK library maintainers in the digital ID space who require a post-quantum-secure proof scheme with a transparent setup that fits within tight mobile CPU, RAM, and bandwidth budgets. We compare seven schemes on a SHA-256 circuit with 2 kB input, measuring prover time, RAM usage, proof size, and preprocessing overhead. Our results show that: -* **Binius** offers ≈5 s proving, sub-50 MB RAM, and small proofs. -* **Ligero** is a runner-up with ≈30-95 s proving and a modest RAM/VRAM footprint. + +- **Binius** offers ≈5 s proving, sub-50 MB RAM, and small proofs. +- **Ligero** is a runner-up with ≈30-95 s proving and a modest RAM/VRAM footprint. No single scheme is perfect: Binius excels at bitwise operations, while Ligero achieves performance by utilizing WebGPU. Developers should choose based on their specific mobile-context trade-offs. Our future work will build upon these benchmarks, so feel free to reach out to the Client-Side Proving team if you have a particular use case or would like to collaborate. @@ -30,10 +36,9 @@ The summary of our requirements is given in the table below. | Requirement | Rationale | | --------------------------------------------------- | ---------------------------------------------------------- | | No trusted setup / long structured reference string | Fewer security assumptions and less mobile bandwidth usage | -| Small proof size | Mobile device limitations | +| Small proof size | Mobile device limitations | | Fast proving | Mobile device limitations | -| Post-quantum soundness | Future-proofing | - +| Post-quantum soundness | Future-proofing | ## Baseline Mobile Hardware @@ -41,15 +46,14 @@ Before choosing a proof scheme, it is essential to understand the limitations of Comprehensive market reports from research firms such as IDC and Counterpoint are prohibitively expensive (e.g., [$7500](https://my.idc.com/getdoc.jsp?containerId=US52082024)), making them inaccessible for this analysis. Therefore, we analyzed the data published by various mobile SDK developers in different industries (analytics, gaming, ads). Our findings were the following: -* Android is dominating the market, while the iPhone's share is around 27%; -* An average iPhone has 6-core CPU @ 3.33 GHz and 5.7 GB RAM; -* An average Android phone has 7-core CPU @ 2.16 GHz and 3.82 GB RAM. +- Android is dominating the market, while the iPhone's share is around 27%; +- An average iPhone has 6-core CPU @ 3.33 GHz and 5.7 GB RAM; +- An average Android phone has 7-core CPU @ 2.16 GHz and 3.82 GB RAM. You can find the data and learn more about our methodology here: https://hackmd.io/@clientsideproving/AvgMobileHardware. An interesting observation arising from our results is that, in case the developer needs to compile the prover to WebAssembly (WASM), WASM's 4 GB RAM limit does not impose a significant additional constraint. Given that an average Android device has approximately 3.82 GB of total RAM, fully utilizing the 4 GB limit is hard in practice anyway. - ## Candidate ZKP Schemes The zero-knowledge proof schemes listed in the table below meet all the requirements we described earlier, including performance, setup, and post-quantum security. @@ -73,48 +77,51 @@ We initially ran benchmarks on an Apple M2 Air laptop (8-core M2 CPU, 24 GB RAM) Note that not all available implementations we tested include the zero-knowledge property by default. Adding the zero-knowledge may result in additional prover overhead, but it is generally lightweight, so the benchmarks remain representative. -### Laptop Benchmarks +### Laptop Benchmarks We ran the benchmarks on an Apple M2 Air laptop (8-core M2 CPU, 24 GB RAM). The Binius circuit performs 33 SHA-256 compressions instead of full hashing; therefore, an actual full implementation would incur additional overhead. We benchmarked the Polyhedra Expander circuit with a 1 kB input due to a prover bug that prevented it from running with a 2 kB input. -| Circuit (GitHub link) | Proving Time | Verification Time | Proof Size | Preprocessing Size | Preprocessing RAM | Prover RAM | Is ZK? | -| ------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | -------------------- | ---------- | -------------------------------------------------- | ----------------- | --------------------------- | --- | -| [Binius (no-lookup)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/binius) | 1.8545 s | 244.48 ms | 475.6 KB | 321.8 KB | ~10.44 MB | ~26.94 MB | No | -| [Binius (lookup)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/binius) | 11.025 s | 572.73 ms | 1.8 MB | 716.86 KB | ~5.02 MB | ~66.14 MB | No | -| [Plonky2 (no-lookup)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/plonky2) | 20.138 s | 5.3135 ms | 175.6 KB | 2.28 GB (prover-only data) + 1.06 KB (common data) | ~2.74 GB | ~2.40 GB | Yes | -| [Plonky3 (SP1 w/precompile)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/plonky3-sp1) | 12.596 s | 184.11 ms | 1.72 MB | 156.34 MB (proving key) + 90.76 KB (ELF) | ~1 GB | ~5 GB | No | -| [Plonky3 (powdr, no precompile)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/plonky3-powdr) | 20.741 s | 256.11 ms | 1.93 MB | 3.1 GB (proving key) + 321 MB (constants) | ~3.87 GB | ~0.32 GB | No | -| [STWO (Cairo)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/stwo) | 21.1 s | N/A (verifier error) | 39.5 MB | 12.6 MB (trace) + 3.4 Mb (memory) | ~600 MB | ~10GB | No | -| [Ligero (Ligetron, *uses WebGPU*)](https://platform.ligetron.com/marketplace/project?id=78180426-2a09-4c36-ac68-52f1ab4ffbe6&version=1.0) | 12.06 s | 9.16 s | 10.29 MB | 33KB (prover data) | N/A | ~500 MB VRAM + ~30 MB RAM | Yes | -| [Polyhedra Expander (Orion + GF2), 1kB input](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/polyhedra-expander) | 70 s | 26 s | 30.75 MB | 6 GB (circuit) | N/A | 15.55 GB | No | +| Circuit (GitHub link) | Proving Time | Verification Time | Proof Size | Preprocessing Size | Preprocessing RAM | Prover RAM | Is ZK? | +| ------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | -------------------- | ---------- | -------------------------------------------------- | ----------------- | ------------------------- | ------ | +| [Binius (no-lookup)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/binius) | 1.8545 s | 244.48 ms | 475.6 KB | 321.8 KB | ~10.44 MB | ~26.94 MB | No | +| [Binius (lookup)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/binius) | 11.025 s | 572.73 ms | 1.8 MB | 716.86 KB | ~5.02 MB | ~66.14 MB | No | +| [Plonky2 (no-lookup)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/plonky2) | 20.138 s | 5.3135 ms | 175.6 KB | 2.28 GB (prover-only data) + 1.06 KB (common data) | ~2.74 GB | ~2.40 GB | Yes | +| [Plonky3 (SP1 w/precompile)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/plonky3-sp1) | 12.596 s | 184.11 ms | 1.72 MB | 156.34 MB (proving key) + 90.76 KB (ELF) | ~1 GB | ~5 GB | No | +| [Plonky3 (powdr, no precompile)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/plonky3-powdr) | 20.741 s | 256.11 ms | 1.93 MB | 3.1 GB (proving key) + 321 MB (constants) | ~3.87 GB | ~0.32 GB | No | +| [STWO (Cairo)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/stwo) | 21.1 s | N/A (verifier error) | 39.5 MB | 12.6 MB (trace) + 3.4 Mb (memory) | ~600 MB | ~10GB | No | +| [Ligero (Ligetron, _uses WebGPU_)](https://platform.ligetron.com/marketplace/project?id=78180426-2a09-4c36-ac68-52f1ab4ffbe6&version=1.0) | 12.06 s | 9.16 s | 10.29 MB | 33KB (prover data) | N/A | ~500 MB VRAM + ~30 MB RAM | Yes | +| [Polyhedra Expander (Orion + GF2), 1kB input](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/polyhedra-expander) | 70 s | 26 s | 30.75 MB | 6 GB (circuit) | N/A | 15.55 GB | No | ### Mobile Benchmarks + We used the following devices for mobile benchmarks: -* iPhone 13 Pro: Hexa-core CPU (2x3.23 GHz Avalanche + 4x1.82 GHz Blizzard), 5-core GPU, 6 GB RAM -* Pixel 6: Octa-core CPU (2x2.80 GHz Cortex-X1 & 2x2.25 GHz Cortex-A76 & 4x1.80 GHz Cortex-A55), Mali-G78 MP20 GPU, 8 GB RAM. + +- iPhone 13 Pro: Hexa-core CPU (2x3.23 GHz Avalanche + 4x1.82 GHz Blizzard), 5-core GPU, 6 GB RAM +- Pixel 6: Octa-core CPU (2x2.80 GHz Cortex-X1 & 2x2.25 GHz Cortex-A76 & 4x1.80 GHz Cortex-A55), Mali-G78 MP20 GPU, 8 GB RAM. The results are in the table below. -| Circuit | Platform | Proving Time | Peak RAM | +| Circuit | Platform | Proving Time | Peak RAM | | ------- | ------------- | ----------------------- | -------- | -| [Binius (no-lookup)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/mobile/binius/android) | Pixel 6 | 5.1023 s | 45 MB | -| [Binius (no-lookup)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/mobile/binius/ios) | iPhone 13 Pro | 5.0124 s | 22 MB | -| [Ligero](https://platform.ligetron.com/marketplace/project?id=78180426-2a09-4c36-ac68-52f1ab4ffbe6&version=1.0) | Pixel 6 | 93.59 s | N/A | -| [Ligero](https://platform.ligetron.com/marketplace/project?id=78180426-2a09-4c36-ac68-52f1ab4ffbe6&version=1.0) | iPhone 13 Pro | 29.77 s | N/A | -| [Plonky2](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/mobile/plonky2/android) | Pixel 6 | Crashed (out of memory) | - | -| [Plonky2](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/mobile/plonky2/ios) | iPhone 13 Pro | Crashed (out of memory) | - | +| [Binius (no-lookup)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/mobile/binius/android) | Pixel 6 | 5.1023 s | 45 MB | +| [Binius (no-lookup)](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/mobile/binius/ios) | iPhone 13 Pro | 5.0124 s | 22 MB | +| [Ligero](https://platform.ligetron.com/marketplace/project?id=78180426-2a09-4c36-ac68-52f1ab4ffbe6&version=1.0) | Pixel 6 | 93.59 s | N/A | +| [Ligero](https://platform.ligetron.com/marketplace/project?id=78180426-2a09-4c36-ac68-52f1ab4ffbe6&version=1.0) | iPhone 13 Pro | 29.77 s | N/A | +| [Plonky2](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/mobile/plonky2/android) | Pixel 6 | Crashed (out of memory) | - | +| [Plonky2](https://github.com/privacy-scaling-explorations/zkid-benchmarks/tree/main/mobile/plonky2/ios) | iPhone 13 Pro | Crashed (out of memory) | - | ## Conclusion Our results reveal that no single scheme perfectly balances mobile resource constraints and proof efficiency. Here is the summary of our findings: -* Binius stands out for SHA-256 circuits thanks to its use of towers of binary fields. Other schemes struggle with bitwise operations, such as XOR and shifts, when working over larger fields. -* Binius no-lookup circuit outperforms its lookup variant because the underlying proof scheme is already optimized for binary operations; adding lookups only incurs extra overhead. -* Ligero exhibits significantly slower proving times on mobile, particularly on Android, despite leveraging WebGPU acceleration and demonstrating good performance on laptops. -* Plonky2 experienced out-of-memory failures, making it unsuitable for the SD-JWT hash signature proving scenario on mobile devices. -* STARK-based solutions (Plonky2, SP1 and powdr) demonstrate expected behavior by demanding multi-gigabyte memory, which places them out of reach for most phones. -* Polyhedra Expander circuit underperforms because it doesn't leverage the layered GKR approach; its long proving time and massive RAM usage reflect a missed optimization opportunity. + +- Binius stands out for SHA-256 circuits thanks to its use of towers of binary fields. Other schemes struggle with bitwise operations, such as XOR and shifts, when working over larger fields. +- Binius no-lookup circuit outperforms its lookup variant because the underlying proof scheme is already optimized for binary operations; adding lookups only incurs extra overhead. +- Ligero exhibits significantly slower proving times on mobile, particularly on Android, despite leveraging WebGPU acceleration and demonstrating good performance on laptops. +- Plonky2 experienced out-of-memory failures, making it unsuitable for the SD-JWT hash signature proving scenario on mobile devices. +- STARK-based solutions (Plonky2, SP1 and powdr) demonstrate expected behavior by demanding multi-gigabyte memory, which places them out of reach for most phones. +- Polyhedra Expander circuit underperforms because it doesn't leverage the layered GKR approach; its long proving time and massive RAM usage reflect a missed optimization opportunity. Overall, the strongest candidates for SHA-256 proving on mobile are Binius and Ligero, yet neither is universally optimal. Developers should select the scheme that best matches their constraints by weighing all the trade-offs. [^1]: https://github.com/eu-digital-identity-wallet -[^2]: https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-19.html#section-4.1.1 +[^2]: https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-19.html#section-4.1.1 diff --git a/articles/hello-world-the-first-signs-of-practical-io.md b/articles/hello-world-the-first-signs-of-practical-io.md index 88bcf76..fc548f8 100644 --- a/articles/hello-world-the-first-signs-of-practical-io.md +++ b/articles/hello-world-the-first-signs-of-practical-io.md @@ -10,8 +10,6 @@ tags: projects: ["machina-io"] --- -# Hello, World: the first signs of practical iO - 04.28.2025 | [Sora Suegami](https://x.com/SoraSue77), [Enrico Bottazzi](https://x.com/backaes), [Pia Park](https://x.com/0xpiapark) Today, we’re excited to announce that **we have successfully implemented [Diamond iO](https://eprint.iacr.org/2025/236)**, a straightforward construction of indistinguishability obfuscation (iO) that we published in February, and **completed its end-to-end workflow**. You can explore the codebase [here](https://github.com/MachinaIO/diamond-io). diff --git a/components/blog/ArticlesList.tsx b/components/blog/ArticlesList.tsx index e90cc71..4dd271d 100644 --- a/components/blog/ArticlesList.tsx +++ b/components/blog/ArticlesList.tsx @@ -159,12 +159,10 @@ async function fetchArticles(tag?: string) { interface ArticlesListProps { tag?: string - fallback?: React.ReactNode } const ArticlesList: React.FC = ({ tag, - fallback = null, }: ArticlesListProps) => { const { data: articles = [], @@ -176,7 +174,7 @@ const ArticlesList: React.FC = ({ }) if (isLoading || articles.length === 0) { - return <>{fallback} + return null } if (isError) { diff --git a/components/blog/article-list-card.tsx b/components/blog/article-list-card.tsx index 6292ca3..721fcaf 100644 --- a/components/blog/article-list-card.tsx +++ b/components/blog/article-list-card.tsx @@ -55,6 +55,11 @@ export const ArticleListCard = ({
( + + {children} + + ), h1: ({ children }) => (

{children} diff --git a/components/project/project-filters-bar.tsx b/components/project/project-filters-bar.tsx index 4baf50c..ab51b67 100644 --- a/components/project/project-filters-bar.tsx +++ b/components/project/project-filters-bar.tsx @@ -81,15 +81,8 @@ export default function ProjectFiltersBar() { const [searchQuery, setSearchQuery] = useState("") const [filterCount, setFilterCount] = useState(0) - const { - filters, - toggleFilter, - queryString, - activeFilters, - onFilterProject, - currentCategory, - setCurrentCategory, - } = useProjectFiltersState((state) => state) + const { filters, toggleFilter, queryString, activeFilters, onFilterProject } = + useProjectFiltersState((state) => state) useEffect(() => { if (!queryString) return @@ -256,38 +249,6 @@ export default function ProjectFiltersBar() {

-
diff --git a/lib/blog.ts b/lib/blog.ts index 7362110..27cfcea 100644 --- a/lib/blog.ts +++ b/lib/blog.ts @@ -15,7 +15,7 @@ export interface Article { publicKey?: string hash?: string canonical?: string - tags?: string[] + tags?: { id: string; name: string }[] projects?: string[] } @@ -71,7 +71,13 @@ export function getArticles(options?: { return { id, ...matterResult.data, - tags: tags, + tags: tags.map((tag) => ({ + id: tag + .toLowerCase() + .replace(/\s+/g, "-") + .replace(/[^a-z0-9-]/g, ""), + name: tag, + })), content: matterResult.content, } } catch (error) { @@ -92,7 +98,7 @@ export function getArticles(options?: { // Filter by tag if provided if (tag) { filteredArticles = filteredArticles.filter((article) => - article.tags?.includes(tag) + article.tags?.some((t) => t.id === tag) ) } @@ -117,6 +123,28 @@ export function getArticles(options?: { .filter((article) => article.id !== "_article-template") } +export const getArticleTags = () => { + const articles = getArticles() + const allTags = + articles + .map((article) => article.tags?.map((t) => t.name)) + .flat() + .filter(Boolean) ?? [] + + return Array.from(new Set(allTags)) as string[] +} + +export const getArticleTagsWithIds = () => { + const tags = getArticleTags() + return tags.map((tag) => ({ + id: tag + .toLowerCase() + .replace(/\s+/g, "-") + .replace(/[^a-z0-9-]/g, ""), + name: tag, + })) +} + export function getArticleById(slug?: string) { // Note: This might need adjustment if you expect getArticleById to also have tags // Currently relies on the base getArticles() which fetches all tags diff --git a/state/useProjectFiltersState.ts b/state/useProjectFiltersState.ts index 3a2ace3..76d392a 100644 --- a/state/useProjectFiltersState.ts +++ b/state/useProjectFiltersState.ts @@ -51,7 +51,6 @@ interface ProjectStateProps { activeFilters: Partial queryString: string searchQuery: string - currentCategory: ProjectCategory | null } interface SearchMatchByParamsProps { @@ -72,7 +71,6 @@ interface ProjectActionsProps { onFilterProject: (searchPattern: string) => void onSelectTheme: (theme: string, searchPattern?: string) => void sortProjectBy: (sortBy: ProjectSortBy) => void - setCurrentCategory: (section: ProjectCategory | null) => void } const createURLQueryString = (params: Partial): string => { @@ -236,7 +234,6 @@ const sortProjectByFn = ({ export const useProjectFiltersState = create< ProjectStateProps & ProjectActionsProps >()((set) => ({ - currentCategory: null, sortBy: DEFAULT_PROJECT_SORT_BY, projects: sortProjectByFn({ projects, @@ -351,16 +348,4 @@ export const useProjectFiltersState = create< } }) }, - setCurrentCategory(category: ProjectCategory | null) { - set((state: any) => { - return { - ...state, - projects: projects.filter((project) => { - if (category == null) return true // return all projects - return project?.category === category - }), - currentCategory: category, - } - }) - }, }))