Skip to content

Commit b70c366

Browse files
authored
Merge pull request #77 from privacy-ethereum/feat/generalized-predicates
Generalised Predicates Integration
2 parents f47f8ae + 69eb9d7 commit b70c366

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+8842
-7340
lines changed

wallet-unit-poc/README.md

Lines changed: 1 addition & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -46,69 +46,7 @@ This section contains comprehensive benchmark results for zkID wallet proof of c
4646

4747
### Desktop Benchmarks (ecdsa-spartan2)
4848

49-
Performance measurements for different JWT payload sizes running on desktop hardware.
50-
51-
**Test Device:** MacBook Pro, M4, 14-core GPU, 24GB RAM
52-
53-
#### Prepare Circuit Timing
54-
55-
All timing measurements are in milliseconds (ms).
56-
57-
| Payload Size | Setup (ms) | Prove (ms) | Reblind (ms) | Verify (ms) |
58-
| ------------ | ---------- | ---------- | ------------ | ----------- |
59-
| 1KB | 2,559 | 1,683 | 382 | 35 |
60-
| 1920 Bytes | 4,157 | 2,727 | 715 | 74 |
61-
| 2KB | 4,384 | 2,934 | 753 | 83 |
62-
| 3KB | 6,466 | 4,242 | 1,357 | 119 |
63-
| 4KB | 8,529 | 5,282 | 1,374 | 131 |
64-
| 5KB | 10,979 | 6,166 | 1,460 | 140 |
65-
| 6KB | 12,993 | 8,407 | 2,821 | 280 |
66-
| 7KB | 15,151 | 8,856 | 2,732 | 230 |
67-
| 8KB | 16,559 | 9,614 | 2,683 | 246 |
68-
69-
#### Show Circuit Timing
70-
71-
The Show circuit has constant performance regardless of JWT payload size.
72-
73-
| Metric | Time (ms) |
74-
| ------- | --------- |
75-
| Setup | ~36 |
76-
| Prove | ~77 |
77-
| Reblind | ~25 |
78-
| Verify | ~9 |
79-
80-
81-
#### Prepare Circuit Sizes
82-
83-
| Payload Size | Proving Key (MB) | Verifying Key (MB) | Proof Size (KB) | Witness Size (MB) |
84-
| ------------ | ---------------- | ------------------ | --------------- | ----------------- |
85-
| 1KB | 252.76 | 252.76 | 75.80 | 32.03 |
86-
| 1920 Bytes | 420.05 | 420.05 | 109.29 | 64.06 |
87-
| 2KB | 433.76 | 433.76 | 109.29 | 64.06 |
88-
| 3KB | 636.35 | 636.35 | 175.77 | 128.13 |
89-
| 4KB | 836.79 | 836.79 | 175.77 | 128.13 |
90-
| 5KB | 964.70 | 964.70 | 175.77 | 128.13 |
91-
| 6KB | 1,222.26 | 1,222.26 | 308.26 | 256.25 |
92-
| 7KB | 1,382.31 | 1,382.31 | 308.26 | 256.25 |
93-
| 8KB | 1,542.35 | 1,542.35 | 308.26 | 256.25 |
94-
95-
#### Show Circuit Sizes
96-
97-
The Show circuit has constant sizes regardless of JWT payload size.
98-
99-
| Metric | Size |
100-
| ------------- | --------- |
101-
| Proving Key | 3.45 MB |
102-
| Verifying Key | 3.45 MB |
103-
| Proof Size | 40.41 KB |
104-
| Witness Size | 512.52 KB |
105-
106-
#### Running Desktop Benchmarks
107-
108-
```sh
109-
cd ecdsa-spartan2
110-
cargo run --release -- benchmark
111-
```
49+
See [ecdsa-spartan2/README.md](./ecdsa-spartan2/README.md#latest-benchmark-results) for up-to-date timings and sizes across all circuit sizes (1k/2k/4k/8k).
11250

11351
### RSA Verifier Circuits (Reference)
11452

wallet-unit-poc/circom/circuits.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"show": {
2828
"file": "show",
2929
"template": "Show",
30-
"params": [128],
30+
"params": [2, 2, 8, 64],
3131
"pubs": ["deviceKeyX", "deviceKeyY"]
3232
},
3333
"ecdsa": {

wallet-unit-poc/circom/circuits/components/age-verifier.circom

Lines changed: 0 additions & 196 deletions
This file was deleted.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
pragma circom 2.1.6;
2+
3+
include "circomlib/circuits/comparators.circom";
4+
include "@zk-email/circuits/utils/array.circom";
5+
include "../utils/utils.circom";
6+
7+
/// @title ClaimValueExtractor
8+
/// @notice Extracts the value (3rd element) from a [salt, key, value] JSON tuple
9+
/// @notice A claim is encoded as: ["<salt>","<key>","<value>"]
10+
/// @param decodedLen: maximum length of decoded claim
11+
/// @input claim: array of decoded claim bytes (from ClaimDecoder output)
12+
/// @output value: array of value bytes extracted from the tuple
13+
/// @output valueLength: length of extracted value
14+
template ClaimValueExtractor(decodedLen) {
15+
signal input claim[decodedLen];
16+
signal input isActive; // 1 = enforce extraction constraints; 0 = output zeros without constraint
17+
signal output value[decodedLen];
18+
signal output valueLength;
19+
20+
// Step 1: Count quotes in ["salt","key","value"]
21+
component isQuoteCmp[decodedLen];
22+
signal isQuote[decodedLen];
23+
signal quoteCount[decodedLen];
24+
25+
for (var i = 0; i < decodedLen; i++) {
26+
isQuoteCmp[i] = IsEqual();
27+
isQuoteCmp[i].in[0] <== claim[i];
28+
isQuoteCmp[i].in[1] <== 34;
29+
isQuote[i] <== isQuoteCmp[i].out;
30+
quoteCount[i] <== (i == 0 ? 0 : quoteCount[i - 1]) + isQuote[i];
31+
}
32+
33+
// We expect exactly 6 quotes in ["salt","key","value"] (only enforced for active slots)
34+
isActive * (quoteCount[decodedLen - 1] - 6) === 0;
35+
36+
// Step 2: Locate 5th quote (value open) and 6th quote (value close)
37+
component isFifthQuote[decodedLen];
38+
component isSixthQuote[decodedLen];
39+
signal atFifth[decodedLen];
40+
signal atSixth[decodedLen];
41+
signal fifthPosAcc[decodedLen];
42+
signal sixthPosAcc[decodedLen];
43+
44+
for (var i = 0; i < decodedLen; i++) {
45+
isFifthQuote[i] = IsEqual();
46+
isFifthQuote[i].in[0] <== quoteCount[i];
47+
isFifthQuote[i].in[1] <== 5;
48+
49+
isSixthQuote[i] = IsEqual();
50+
isSixthQuote[i].in[0] <== quoteCount[i];
51+
isSixthQuote[i].in[1] <== 6;
52+
53+
atFifth[i] <== isFifthQuote[i].out * isQuote[i];
54+
atSixth[i] <== isSixthQuote[i].out * isQuote[i];
55+
56+
fifthPosAcc[i] <== (i == 0 ? 0 : fifthPosAcc[i - 1]) + atFifth[i] * i;
57+
sixthPosAcc[i] <== (i == 0 ? 0 : sixthPosAcc[i - 1]) + atSixth[i] * i;
58+
}
59+
60+
signal startPos <== fifthPosAcc[decodedLen - 1] + 1;
61+
signal endPos <== sixthPosAcc[decodedLen - 1];
62+
signal rawLength <== endPos - startPos;
63+
// Gate through isActive: inactive slots get valueLength=0, which zeroes all value outputs.
64+
valueLength <== isActive * rawLength;
65+
66+
// Step 3: Shift so value begins at index 0, then mask after valueLength
67+
component shifter = VarShiftLeft(decodedLen, decodedLen);
68+
shifter.in <== claim;
69+
shifter.shift <== startPos;
70+
71+
component lengthGt[decodedLen];
72+
for (var i = 0; i < decodedLen; i++) {
73+
lengthGt[i] = GreaterThan(log2Ceil(decodedLen + 1));
74+
lengthGt[i].in[0] <== valueLength;
75+
lengthGt[i].in[1] <== i;
76+
value[i] <== lengthGt[i].out * shifter.out[i];
77+
}
78+
}
79+

0 commit comments

Comments
 (0)