We use Poseidon2 instantiation with the following parameter:
The implementation of this specific instantiation could be found here.
succinctlabs/sp1
(based on plonky3
)openvm-org/openvm
(based on plonky3
)plonky3
In order to handle non-uniform computations efficiently, we split the circuit into multiple AIR traces and use lookup to connect input/output between each other.
AIR traces are:
Main
Chain
MerkleTree
Decomposition
RangeCheck
Main
The Main
is mainly used to synchronize other AIR traces, it does 3 things:
(sig_idx, msg_hash)
to Decomposition
to make sure the x
are decomposed from msg_hash
.(sig_idx, parameter)
to Chain
to make sure it's using the same parameter
as others.(sig_idx, parameter, msg_hash, merkle_root)
to MerkleTree
to make sure the msg_hash
is computed from the same parameter
as others, and at the same time send to expected merkle_root
to be checked for sigs[sig_idx]
.And parameter
and merkle_root
parts are the statement of this aggregation circuit, verifier can calculate their polynomial evaluations in tiem by FFT.
Chain
The Chain
receives x
(msg_hash
chunks) from Decomposition
, computes rest of the chain if the one_time_sig_i
of i
-th chain is not yet the end, then sends the one_time_pk_i
to MerkleTree
.
We ignore the chain if it's already in the end, so the number of rows for a signature could be constant (equal to the target sum).
A sample trace with x = (0, 2, 1, 3, 3, 0, ..., 1, 3, 3)
might look like:
MerkleTree
The MerkleTree
receives 2 things:
one_time_pk_i
where x_i
is not yet end from Chain
, and hashes them with one_time_pk_i
where x_i
is already end into merkle_leaf_hash
, and then computes the merkle_root
to be used latter.(parameter, merkle_root, msg_hash)
as synchronization, where the merkle_root
is computed from merkle_leaf_hash
and siblings
, parameter
is used in all tweakable hashes, and msg_hash
is computed separately in the next adjacent row.A sample trace with epoch = 0b0...01
might look like:
Decomposition
The Decomposition
receives msg_hash
as values
, and calculates
(((values[4] * P) + values[3]) * P + ...) * P + values[0]
, then decompose it into base 2w
chunks and send non-ending chunk to Chain
.
A sample trace with x = (0, 2, 1, 3, 3, 0, ..., 1, 3, 3)
might look like:
RangeCheck
The RangeCheck
is filled by range 0..212
to receives limb
from Decomposition
as range check.
See https://hackmd.io/@tcoratger/SyWbmVPckx for more details.
124
(degree-4 extension of KoalaBear)256 / R
The benchmark aggregates 8192 signatures, to reproduce please run the following script in <repo>/circuit
:
R |
T |
PT |
TP |
PS |
PM |
VT |
---|---|---|---|---|---|---|
1 |
4 |
5.33 s |
1536 |
1.68 MB |
5.76 GB |
94.04 ms |
1 |
8 |
3.00 s |
2729 |
1.68 MB |
5.78 GB |
99.68 ms |
1 |
16 |
2.65 s |
3086 |
1.68 MB |
5.17 GB |
94.55 ms |
1 |
24 |
2.19 s |
3745 |
1.68 MB |
5.93 GB |
96.09 ms |
2 |
4 |
8.59 s |
953 |
930.96 kB |
8.91 GB |
49.09 ms |
2 |
8 |
4.98 s |
1644 |
933.96 kB |
8.81 GB |
59.41 ms |
2 |
16 |
4.24 s |
1932 |
933.71 kB |
8.97 GB |
72.45 ms |
2 |
24 |
3.36 s |
2439 |
933.96 kB |
12.48 GB |
50.08 ms |
3 |
4 |
14.99 s |
546 |
672.30 kB |
16.07 GB |
53.88 ms |
3 |
8 |
8.75 s |
936 |
671.24 kB |
16.27 GB |
35.08 ms |
3 |
16 |
7.22 s |
1134 |
670.96 kB |
16.18 GB |
40.86 ms |
3 |
24 |
6.17 s |
1327 |
671.02 kB |
16.21 GB |
34.79 ms |
R
: Log 1/rate T
: Threads PT
: Proving time TP
: Throughput (sig/s) PS
: Proof size PM
: Peak proving memory VT
: Verifying time