diff --git a/Cargo.lock b/Cargo.lock index b3cd7862eb..1f3226d480 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3941,15 +3941,18 @@ dependencies = [ "e3-utils", "e3-zk-helpers", "fhe", + "fhe-traits", "flate2", "futures-util", "hex", "indicatif 0.17.11", "nargo", + "ndarray", "noirc_abi", "num-bigint", "num-traits", "paste", + "rand 0.8.5", "reqwest", "serde", "serde_json", diff --git a/agent/flow-trace/04_DKG_AND_COMPUTATION.md b/agent/flow-trace/04_DKG_AND_COMPUTATION.md index 2f77cf1ef6..f4cc411a43 100644 --- a/agent/flow-trace/04_DKG_AND_COMPUTATION.md +++ b/agent/flow-trace/04_DKG_AND_COMPUTATION.md @@ -262,15 +262,24 @@ ProofRequestActor receives ThresholdSharePending → Ensures no incomplete data is gossiped ``` -**C2 inner vs wrapper:** For each C2a/C2b request, the prover builds a **recursive** proof for -`sk_share_computation` / `e_sm_share_computation`. That **inner** `Proof` is what -`PendingThresholdProofs` stores and what gets ECDSA-signed for gossip -(`ProofType::C2aSkShareComputation` / `C2bESmShareComputation`). When node proof aggregation is -enabled, the prover also returns a **wrapper** proof (`CircuitName::ShareComputation`); the actor -publishes `DKGInnerProofReady` with that wrapper for folding, but does not replace the signed inner -payload. Verifiers treat C2a/C2b as allowing both inner and wrapper circuit names -(`ProofType::circuit_names()`); multithread ZK verify uses standard `bb verify` for inner circuits -and the wrapper VK path when the bundle’s circuit is `ShareComputation`. +**C2 proofs:** For each C2a/C2b request, the prover builds a **recursive** proof for +`sk_share_computation` / `e_sm_share_computation`. That `Proof` is what `PendingThresholdProofs` +stores and what gets ECDSA-signed for gossip (`ProofType::C2aSkShareComputation` / +`C2bESmShareComputation`). The old generic `recursive_aggregation/wrapper/*` circuits and two-proof +`recursive_aggregation/fold` were removed; aggregation is done by ad-hoc Noir bins under +`circuits/bin/recursive_aggregation/` (e.g. `c2ab_fold`, `c3ab_fold`, `c6_fold`, `node_fold`, +`nodes_fold`, `dkg_aggregator`, `decryption_aggregator` — `nodes_fold` chains `H` `node_fold` proofs +for `dkg_aggregator`; `decryption_aggregator` folds C6 via non-ZK `c6_fold` then checks C7 with ZK). +The per-circuit `wrapper/` Noir step was removed; aggregator response structs no longer carry a +`wrapped_proof` field — the inner recursive proof itself is what flows between stages. + +**Ciphernode / aggregator integration:** `ZkRequest::FoldProofs` was removed. The multithread actor +implements `ZkRequest::NodeDkgFold` (full per-node pipeline to a `NodeFold` proof), +`ZkRequest::DkgAggregation` (`NodesFold` + C5 + `DkgAggregator`), and +`ZkRequest::DecryptionAggregation` (per-ciphertext `C6Fold` + C7 + `DecryptionAggregator`). +`NodeProofAggregator` buffers all `DKGInnerProofReady` proofs then issues one `NodeDkgFold` request; +`PublicKeyAggregator` and `ThresholdPlaintextAggregator` dispatch the aggregator requests instead of +pairwise folding. ### Step 6: Collect All Threshold Shares (with C2/C3 Verification) @@ -348,6 +357,7 @@ ShareVerificationActor receives ShareVerificationDispatched(kind=ShareProofs) │ │ │ C4a→C6 (SameParty): C4a's commitment == C6's expected_sk_commitment │ │ │ C4b→C6 (SameParty): C4b's commitment == C6's expected_e_sm_commitment │ │ │ C6→C7 (CrossParty): C6's d_commitment matches C7's expected_d_commitment +│ │ │ (on-chain / E3 state) C3 `ct_commitment` output and C6 `ct_commitment` input bind to the same ciphertext as user_data_encryption (not a CommitmentLink row) │ │ │ │ │ ├─ On mismatch: publishes CommitmentConsistencyViolation │ │ │ → AccusationManager initiates accusation quorum (see Part 5) @@ -363,8 +373,8 @@ ShareVerificationActor receives ShareVerificationDispatched(kind=ShareProofs) │ │ party_proofs, // consistency-passing parties' ZK proof data │ │ }) │ │ -│ ├─ Multithread ZK verify: inner circuits → `bb verify` with recursive VK; -│ │ `ShareComputation` (wrapper) → wrapper VK / `verify_wrapper_proof` path +│ ├─ Multithread ZK verify: `bb verify` on inner recursive circuits (same path as +│ │ `ZkProver::verify_proof`) │ │ → Returns per-party pass/fail results │ │ │ └─ On ComputeResponse: diff --git a/circuits/bin/dkg/share_encryption/src/main.nr b/circuits/bin/dkg/share_encryption/src/main.nr index ad96bbb1f2..846f9d32a3 100644 --- a/circuits/bin/dkg/share_encryption/src/main.nr +++ b/circuits/bin/dkg/share_encryption/src/main.nr @@ -18,8 +18,8 @@ fn main( expected_message_commitment: pub Field, pk0is: [Polynomial; L], pk1is: [Polynomial; L], - ct0is: pub [Polynomial; L], - ct1is: pub [Polynomial; L], + ct0is: [Polynomial; L], + ct1is: [Polynomial; L], u: Polynomial, e0: Polynomial, e0is: [Polynomial; L], @@ -30,7 +30,7 @@ fn main( r2is: [Polynomial; L], p1is: [Polynomial<(2 * N) - 1>; L], p2is: [Polynomial; L], -) { +) -> pub Field { let share_encryption: ShareEncryption = ShareEncryption::new( SHARE_ENCRYPTION_CONFIGS, expected_pk_commitment, @@ -50,5 +50,5 @@ fn main( p1is, p2is, ); - share_encryption.execute(); + share_encryption.execute() } diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/pk_generation/Nargo.toml b/circuits/bin/recursive_aggregation/c2ab_fold/Nargo.toml similarity index 80% rename from circuits/bin/recursive_aggregation/wrapper/threshold/pk_generation/Nargo.toml rename to circuits/bin/recursive_aggregation/c2ab_fold/Nargo.toml index 2a8342e71f..31e719c986 100644 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/pk_generation/Nargo.toml +++ b/circuits/bin/recursive_aggregation/c2ab_fold/Nargo.toml @@ -1,8 +1,8 @@ [package] -name = "pk_generation" +name = "c2ab_fold" type = "bin" authors = ["Gnosis Guild / Enclave"] [dependencies] -lib = { path = "../../../../../lib" } +lib = { path = "../../../lib" } bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/c2ab_fold/src/main.nr b/circuits/bin/recursive_aggregation/c2ab_fold/src/main.nr new file mode 100644 index 0000000000..f3a7765358 --- /dev/null +++ b/circuits/bin/recursive_aggregation/c2ab_fold/src/main.nr @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Combines C2a + C2b (two ZK Honk proofs) and surfaces their public inputs for `node_fold`. + +use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; +use lib::configs::default::dkg::L_THRESHOLD; +use lib::configs::default::N_PARTIES; +use lib::math::commitments::compute_vk_hash; + +/// C2 inner public IO: `expected_secret_commitment` + `[[Field; L_THRESHOLD]; N_PARTIES]`. +pub global C2_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); + +/// Two key-hash params + combined key hash + two C2-sized public vectors (a and b). +pub global C2AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C2_PUBLIC_LEN); + +fn main( + c2a_vk: UltraHonkVerificationKey, + c2a_proof: UltraHonkZKProof, + c2a_public: [Field; C2_PUBLIC_LEN], + c2b_vk: UltraHonkVerificationKey, + c2b_proof: UltraHonkZKProof, + c2b_public: [Field; C2_PUBLIC_LEN], + c2a_key_hash: pub Field, + c2b_key_hash: pub Field, + ) -> pub (Field, Field, [[Field; L_THRESHOLD]; N_PARTIES], Field, [[Field; L_THRESHOLD]; N_PARTIES]) { + verify_honk_proof(c2a_vk, c2a_proof, c2a_public, c2a_key_hash); + verify_honk_proof(c2b_vk, c2b_proof, c2b_public, c2b_key_hash); + + let mut vk_hashes = Vec::new(); + vk_hashes.push(c2a_key_hash); + vk_hashes.push(c2b_key_hash); + let key_hash = compute_vk_hash(vk_hashes); + + let c2a_expected = c2a_public[0]; + let mut c2a_shares: [[Field; L_THRESHOLD]; N_PARTIES] = [[0; L_THRESHOLD]; N_PARTIES]; + for j in 0..N_PARTIES { + for l in 0..L_THRESHOLD { + c2a_shares[j][l] = c2a_public[1 + j * L_THRESHOLD + l]; + } + } + + let c2b_expected = c2b_public[0]; + let mut c2b_shares: [[Field; L_THRESHOLD]; N_PARTIES] = [[0; L_THRESHOLD]; N_PARTIES]; + for j in 0..N_PARTIES { + for l in 0..L_THRESHOLD { + c2b_shares[j][l] = c2b_public[1 + j * L_THRESHOLD + l]; + } + } + + (key_hash, c2a_expected, c2a_shares, c2b_expected, c2b_shares) +} diff --git a/circuits/bin/recursive_aggregation/fold/Nargo.toml b/circuits/bin/recursive_aggregation/c3_fold/Nargo.toml similarity index 94% rename from circuits/bin/recursive_aggregation/fold/Nargo.toml rename to circuits/bin/recursive_aggregation/c3_fold/Nargo.toml index 8bfe19c7f9..a6564fb540 100644 --- a/circuits/bin/recursive_aggregation/fold/Nargo.toml +++ b/circuits/bin/recursive_aggregation/c3_fold/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "fold" +name = "c3_fold" type = "bin" authors = ["Gnosis Guild / Enclave"] diff --git a/circuits/bin/recursive_aggregation/c3_fold/src/main.nr b/circuits/bin/recursive_aggregation/c3_fold/src/main.nr new file mode 100644 index 0000000000..acb49aa817 --- /dev/null +++ b/circuits/bin/recursive_aggregation/c3_fold/src/main.nr @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Sequential C3 fold: one inner ZK `ShareEncryption` proof per step, chained with the previous +//! accumulator (`c3_fold` non-ZK proof, or a fixed genesis proof from `c3_fold_kernel` on the first +//! step; see `crates/zk-prover` and `c3_fold_kernel`). + +use bb_proof_verification::{ + UltraHonkProof, UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof, + verify_honk_proof_non_zk, +}; +use lib::configs::default::dkg::L_THRESHOLD; +use lib::configs::default::N_PARTIES; + +/// Total slots: one per (party, threshold-modulus) pair. +pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD; + +/// Total public fields for a `c3_fold` proof: 4 pub parameters + 3*C3_SLOTS (pk, msg, ct). +pub global C3_FOLD_PUBLIC_LEN: u32 = 4 + (3 * C3_SLOTS); + +pub global C3_FOLD_PREFIX_LEN: u32 = C3_FOLD_PUBLIC_LEN - (3 * C3_SLOTS); + +fn main( + inner_vk: UltraHonkVerificationKey, + inner_proof: UltraHonkZKProof, + c3_public_inputs: [Field; 3], + acc_vk: UltraHonkVerificationKey, + acc_proof: UltraHonkProof, + acc_public_inputs: [Field; C3_FOLD_PUBLIC_LEN], + inner_key_hash: pub Field, + acc_key_hash: pub Field, + is_first_step: pub bool, + slot_index: pub u32, +) -> pub ([Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS]) { + assert(slot_index < C3_SLOTS); + + verify_honk_proof(inner_vk, inner_proof, c3_public_inputs, inner_key_hash); + + verify_honk_proof_non_zk(acc_vk, acc_proof, acc_public_inputs, acc_key_hash); + + let pk_new = c3_public_inputs[0]; + let msg_new = c3_public_inputs[1]; + let ct_new = c3_public_inputs[2]; + + let mut out_pk: [Field; C3_SLOTS] = [0; C3_SLOTS]; + let mut out_msg: [Field; C3_SLOTS] = [0; C3_SLOTS]; + let mut out_ct: [Field; C3_SLOTS] = [0; C3_SLOTS]; + + for i in 0..C3_SLOTS { + let idx = i as u32; + if is_first_step { + if idx == slot_index { + out_pk[i] = pk_new; + out_msg[i] = msg_new; + out_ct[i] = ct_new; + } else { + out_pk[i] = 0; + out_msg[i] = 0; + out_ct[i] = 0; + } + } else { + let prior_pk = acc_public_inputs[C3_FOLD_PREFIX_LEN + i]; + let prior_msg = acc_public_inputs[C3_FOLD_PREFIX_LEN + C3_SLOTS + i]; + let prior_ct = acc_public_inputs[C3_FOLD_PREFIX_LEN + C3_SLOTS + C3_SLOTS + i]; + + if idx == slot_index { + assert(prior_pk == 0); + assert(prior_msg == 0); + assert(prior_ct == 0); + out_pk[i] = pk_new; + out_msg[i] = msg_new; + out_ct[i] = ct_new; + } else { + out_pk[i] = prior_pk; + out_msg[i] = prior_msg; + out_ct[i] = prior_ct; + } + } + } + + (out_pk, out_msg, out_ct) +} diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/pk_aggregation/Nargo.toml b/circuits/bin/recursive_aggregation/c3_fold_kernel/Nargo.toml similarity index 80% rename from circuits/bin/recursive_aggregation/wrapper/threshold/pk_aggregation/Nargo.toml rename to circuits/bin/recursive_aggregation/c3_fold_kernel/Nargo.toml index 00bb554013..d62582323e 100644 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/pk_aggregation/Nargo.toml +++ b/circuits/bin/recursive_aggregation/c3_fold_kernel/Nargo.toml @@ -1,8 +1,8 @@ [package] -name = "pk_aggregation" +name = "c3_fold_kernel" type = "bin" authors = ["Gnosis Guild / Enclave"] [dependencies] -lib = { path = "../../../../../lib" } +lib = { path = "../../../lib" } bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/c3_fold_kernel/src/main.nr b/circuits/bin/recursive_aggregation/c3_fold_kernel/src/main.nr new file mode 100644 index 0000000000..6fb95dce68 --- /dev/null +++ b/circuits/bin/recursive_aggregation/c3_fold_kernel/src/main.nr @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Same IO as [`c3_fold`](../c3_fold/src/main.nr) but **only** verifies the inner `ShareEncryption` +//! proof. Used to mint a fixed genesis `UltraHonkProof` that bootstraps the real `c3_fold` circuit, +//! which always runs `verify_honk_proof_non_zk` on the accumulator (see `c3_fold`). + +use bb_proof_verification::{ + UltraHonkProof, UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof, +}; +use lib::configs::default::dkg::L_THRESHOLD; +use lib::configs::default::N_PARTIES; + +pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD; + +pub global C3_FOLD_PUBLIC_LEN: u32 = 4 + (3 * C3_SLOTS); + +fn main( + inner_vk: UltraHonkVerificationKey, + inner_proof: UltraHonkZKProof, + c3_public_inputs: [Field; 3], + acc_vk: UltraHonkVerificationKey, + acc_proof: UltraHonkProof, + acc_public_inputs: [Field; C3_FOLD_PUBLIC_LEN], + inner_key_hash: pub Field, + acc_key_hash: pub Field, + is_first_step: pub bool, + slot_index: pub u32, +) -> pub ([Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS]) { + assert(slot_index < C3_SLOTS); + + verify_honk_proof(inner_vk, inner_proof, c3_public_inputs, inner_key_hash); + + // Signature parity with `c3_fold`: kernel mints the genesis accumulator, so acc_* is unused. + let _ = (acc_vk, acc_proof, acc_key_hash, acc_public_inputs); + + assert(is_first_step); + + let pk_new = c3_public_inputs[0]; + let msg_new = c3_public_inputs[1]; + let ct_new = c3_public_inputs[2]; + + let mut out_pk: [Field; C3_SLOTS] = [0; C3_SLOTS]; + let mut out_msg: [Field; C3_SLOTS] = [0; C3_SLOTS]; + let mut out_ct: [Field; C3_SLOTS] = [0; C3_SLOTS]; + + for i in 0..C3_SLOTS { + let idx = i as u32; + if idx == slot_index { + out_pk[i] = pk_new; + out_msg[i] = msg_new; + out_ct[i] = ct_new; + } else { + out_pk[i] = 0; + out_msg[i] = 0; + out_ct[i] = 0; + } + } + + (out_pk, out_msg, out_ct) +} diff --git a/circuits/bin/recursive_aggregation/c3ab_fold/Nargo.toml b/circuits/bin/recursive_aggregation/c3ab_fold/Nargo.toml new file mode 100644 index 0000000000..5bf93138a2 --- /dev/null +++ b/circuits/bin/recursive_aggregation/c3ab_fold/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "c3ab_fold" +type = "bin" +authors = ["Gnosis Guild / Enclave"] + +[dependencies] +lib = { path = "../../../lib" } +bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/c3ab_fold/src/main.nr b/circuits/bin/recursive_aggregation/c3ab_fold/src/main.nr new file mode 100644 index 0000000000..de419e3483 --- /dev/null +++ b/circuits/bin/recursive_aggregation/c3ab_fold/src/main.nr @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Combines the final `c3_fold` proof for sk C3 chain with the final `c3_fold` for e_sm. + +use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use lib::configs::default::dkg::L_THRESHOLD; +use lib::configs::default::N_PARTIES; +use lib::math::commitments::compute_vk_hash; + +/// Must match `c3_fold`: one slot per (party, threshold-modulus) pair. +pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD; + +/// Must match `c3_fold`: `4 + 3*C3_SLOTS` public fields per final proof. +pub global C3_FOLD_PUBLIC_LEN: u32 = 4 + (3 * C3_SLOTS); + +pub global C3_FOLD_PREFIX_LEN: u32 = C3_FOLD_PUBLIC_LEN - (3 * C3_SLOTS); + +/// Total public fields for `c3ab_fold` (two key hashes + flattened return tuple). +pub global C3AB_FOLD_PUBLIC_LEN: u32 = 2 + 1 + (6 * C3_SLOTS); + +fn main( + c3a_vk: UltraHonkVerificationKey, + c3a_proof: UltraHonkProof, + c3a_public: [Field; C3_FOLD_PUBLIC_LEN], + c3b_vk: UltraHonkVerificationKey, + c3b_proof: UltraHonkProof, + c3b_public: [Field; C3_FOLD_PUBLIC_LEN], + c3a_key_hash: pub Field, + c3b_key_hash: pub Field, + ) -> pub (Field, [Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS]) { + verify_honk_proof_non_zk(c3a_vk, c3a_proof, c3a_public, c3a_key_hash); + verify_honk_proof_non_zk(c3b_vk, c3b_proof, c3b_public, c3b_key_hash); + + let mut vk_hashes = Vec::new(); + vk_hashes.push(c3a_key_hash); + vk_hashes.push(c3b_key_hash); + let key_hash = compute_vk_hash(vk_hashes); + + let mut pk_a: [Field; C3_SLOTS] = [0; C3_SLOTS]; + let mut msg_a: [Field; C3_SLOTS] = [0; C3_SLOTS]; + let mut ct_a: [Field; C3_SLOTS] = [0; C3_SLOTS]; + let mut pk_b: [Field; C3_SLOTS] = [0; C3_SLOTS]; + let mut msg_b: [Field; C3_SLOTS] = [0; C3_SLOTS]; + let mut ct_b: [Field; C3_SLOTS] = [0; C3_SLOTS]; + + for j in 0..C3_SLOTS { + pk_a[j] = c3a_public[C3_FOLD_PREFIX_LEN + j]; + msg_a[j] = c3a_public[C3_FOLD_PREFIX_LEN + C3_SLOTS + j]; + ct_a[j] = c3a_public[C3_FOLD_PREFIX_LEN + C3_SLOTS + C3_SLOTS + j]; + pk_b[j] = c3b_public[C3_FOLD_PREFIX_LEN + j]; + msg_b[j] = c3b_public[C3_FOLD_PREFIX_LEN + C3_SLOTS + j]; + ct_b[j] = c3b_public[C3_FOLD_PREFIX_LEN + C3_SLOTS + C3_SLOTS + j]; + } + + (key_hash, pk_a, msg_a, ct_a, pk_b, msg_b, ct_b) +} diff --git a/circuits/bin/recursive_aggregation/c4ab_fold/Nargo.toml b/circuits/bin/recursive_aggregation/c4ab_fold/Nargo.toml new file mode 100644 index 0000000000..edcdf3b3f0 --- /dev/null +++ b/circuits/bin/recursive_aggregation/c4ab_fold/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "c4ab_fold" +type = "bin" +authors = ["Gnosis Guild / Enclave"] + +[dependencies] +lib = { path = "../../../lib" } +bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/c4ab_fold/src/main.nr b/circuits/bin/recursive_aggregation/c4ab_fold/src/main.nr new file mode 100644 index 0000000000..a23c980edf --- /dev/null +++ b/circuits/bin/recursive_aggregation/c4ab_fold/src/main.nr @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Combines C4a + C4b (two ZK Honk proofs using the same C4 binary). + +use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; +use lib::configs::default::dkg::L_THRESHOLD; +use lib::configs::default::H; +use lib::math::commitments::compute_vk_hash; + +/// C4 public IO: `expected_commitments` (flattened) + `agg_commit`. +pub global C4_PUBLIC_LEN: u32 = (H * L_THRESHOLD) + 1; + +/// Two key-hash params + combined key hash + two C4-sized public vectors (a and b). +pub global C4AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C4_PUBLIC_LEN); + +fn main( + c4a_vk: UltraHonkVerificationKey, + c4a_proof: UltraHonkZKProof, + c4a_public: [Field; C4_PUBLIC_LEN], + c4b_vk: UltraHonkVerificationKey, + c4b_proof: UltraHonkZKProof, + c4b_public: [Field; C4_PUBLIC_LEN], + c4a_key_hash: pub Field, + c4b_key_hash: pub Field, +) -> pub (Field, [[Field; L_THRESHOLD]; H], Field, [[Field; L_THRESHOLD]; H], Field) { + verify_honk_proof(c4a_vk, c4a_proof, c4a_public, c4a_key_hash); + verify_honk_proof(c4b_vk, c4b_proof, c4b_public, c4b_key_hash); + + let mut vk_hashes = Vec::new(); + vk_hashes.push(c4a_key_hash); + vk_hashes.push(c4b_key_hash); + let key_hash = compute_vk_hash(vk_hashes); + + let mut c4a_expected: [[Field; L_THRESHOLD]; H] = [[0; L_THRESHOLD]; H]; + for h in 0..H { + for l in 0..L_THRESHOLD { + c4a_expected[h][l] = c4a_public[h * L_THRESHOLD + l]; + } + } + let sk_agg = c4a_public[H * L_THRESHOLD]; + + let mut c4b_expected: [[Field; L_THRESHOLD]; H] = [[0; L_THRESHOLD]; H]; + for h in 0..H { + for l in 0..L_THRESHOLD { + c4b_expected[h][l] = c4b_public[h * L_THRESHOLD + l]; + } + } + let esm_agg = c4b_public[H * L_THRESHOLD]; + + (key_hash, c4a_expected, sk_agg, c4b_expected, esm_agg) +} diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/pk/Nargo.toml b/circuits/bin/recursive_aggregation/c6_fold/Nargo.toml similarity index 83% rename from circuits/bin/recursive_aggregation/wrapper/dkg/pk/Nargo.toml rename to circuits/bin/recursive_aggregation/c6_fold/Nargo.toml index 0b805350df..4af497ae8b 100644 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/pk/Nargo.toml +++ b/circuits/bin/recursive_aggregation/c6_fold/Nargo.toml @@ -1,8 +1,8 @@ [package] -name = "pk" +name = "c6_fold" type = "bin" authors = ["Gnosis Guild / Enclave"] [dependencies] -lib = { path = "../../../../../lib" } +lib = { path = "../../../lib" } bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/c6_fold/src/main.nr b/circuits/bin/recursive_aggregation/c6_fold/src/main.nr new file mode 100644 index 0000000000..932548cd75 --- /dev/null +++ b/circuits/bin/recursive_aggregation/c6_fold/src/main.nr @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Sequential C6 fold: one inner ZK `ThresholdShareDecryption` proof per step, chained with the +//! previous accumulator (`c6_fold` non-ZK proof, or a fixed genesis proof from `c6_fold_kernel` on +//! the first step; see `crates/zk-prover` and `c6_fold_kernel`). + +use bb_proof_verification::{ + UltraHonkProof, UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof, + verify_honk_proof_non_zk, +}; +use lib::configs::default::T; + +/// One C6 public row: `expected_sk`, `expected_e_sm`, `ct_commitment`, `d_commitment` (see C6 leaf). +pub global C6_PUBLIC_LEN: u32 = 4; + +/// Same shape as `c3_fold`: 4 pub parameters + 4 columns of `T + 1` party slots each. +pub global C6_FOLD_PUBLIC_LEN: u32 = 4 + (4 * (T + 1)); + +pub global C6_FOLD_PREFIX_LEN: u32 = C6_FOLD_PUBLIC_LEN - (4 * (T + 1)); + +fn main( + inner_vk: UltraHonkVerificationKey, + inner_proof: UltraHonkZKProof, + c6_public_inputs: [Field; C6_PUBLIC_LEN], + acc_vk: UltraHonkVerificationKey, + acc_proof: UltraHonkProof, + acc_public_inputs: [Field; C6_FOLD_PUBLIC_LEN], + inner_key_hash: pub Field, + acc_key_hash: pub Field, + is_first_step: pub bool, + slot_index: pub u32, +) -> pub ([Field; T + 1], [Field; T + 1], [Field; T + 1], [Field; T + 1]) { + assert(slot_index < T + 1); + + verify_honk_proof(inner_vk, inner_proof, c6_public_inputs, inner_key_hash); + + verify_honk_proof_non_zk(acc_vk, acc_proof, acc_public_inputs, acc_key_hash); + + let sk_new = c6_public_inputs[0]; + let esm_new = c6_public_inputs[1]; + let ct_new = c6_public_inputs[2]; + let d_new = c6_public_inputs[3]; + + let mut out_sk: [Field; T + 1] = [0; T + 1]; + let mut out_esm: [Field; T + 1] = [0; T + 1]; + let mut out_ct: [Field; T + 1] = [0; T + 1]; + let mut out_d: [Field; T + 1] = [0; T + 1]; + + for i in 0..(T + 1) { + let idx = i as u32; + if is_first_step { + if idx == slot_index { + out_sk[i] = sk_new; + out_esm[i] = esm_new; + out_ct[i] = ct_new; + out_d[i] = d_new; + } else { + out_sk[i] = 0; + out_esm[i] = 0; + out_ct[i] = 0; + out_d[i] = 0; + } + } else { + let prior_sk = acc_public_inputs[C6_FOLD_PREFIX_LEN + i]; + let prior_esm = acc_public_inputs[C6_FOLD_PREFIX_LEN + (T + 1) + i]; + let prior_ct = acc_public_inputs[C6_FOLD_PREFIX_LEN + (2 * (T + 1)) + i]; + let prior_d = acc_public_inputs[C6_FOLD_PREFIX_LEN + (3 * (T + 1)) + i]; + + if idx == slot_index { + assert(prior_sk == 0); + assert(prior_esm == 0); + assert(prior_ct == 0); + assert(prior_d == 0); + out_sk[i] = sk_new; + out_esm[i] = esm_new; + out_ct[i] = ct_new; + out_d[i] = d_new; + } else { + out_sk[i] = prior_sk; + out_esm[i] = prior_esm; + out_ct[i] = prior_ct; + out_d[i] = prior_d; + } + } + } + + (out_sk, out_esm, out_ct, out_d) +} diff --git a/circuits/bin/recursive_aggregation/c6_fold_kernel/Nargo.toml b/circuits/bin/recursive_aggregation/c6_fold_kernel/Nargo.toml new file mode 100644 index 0000000000..284ce5e2fd --- /dev/null +++ b/circuits/bin/recursive_aggregation/c6_fold_kernel/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "c6_fold_kernel" +type = "bin" +authors = ["Gnosis Guild / Enclave"] + +[dependencies] +lib = { path = "../../../lib" } +bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/c6_fold_kernel/src/main.nr b/circuits/bin/recursive_aggregation/c6_fold_kernel/src/main.nr new file mode 100644 index 0000000000..6abb49fbe7 --- /dev/null +++ b/circuits/bin/recursive_aggregation/c6_fold_kernel/src/main.nr @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Same IO as [`c6_fold`](../c6_fold/src/main.nr) but **only** verifies the inner +//! `ThresholdShareDecryption` proof. Used to mint a fixed genesis `UltraHonkProof` that bootstraps +//! the real `c6_fold` circuit, which always runs `verify_honk_proof_non_zk` on the accumulator (see +//! `c6_fold`). + +use bb_proof_verification::{ + UltraHonkProof, UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof, +}; +use lib::configs::default::T; + +/// One C6 public row: `expected_sk`, `expected_e_sm`, `ct_commitment`, `d_commitment` (see C6 leaf). +pub global C6_PUBLIC_LEN: u32 = 4; + +/// Same shape as `c6_fold`: 4 pub parameters + 4 columns of `T + 1` party slots each. +pub global C6_FOLD_PUBLIC_LEN: u32 = 4 + (4 * (T + 1)); + +fn main( + inner_vk: UltraHonkVerificationKey, + inner_proof: UltraHonkZKProof, + c6_public_inputs: [Field; C6_PUBLIC_LEN], + acc_vk: UltraHonkVerificationKey, + acc_proof: UltraHonkProof, + acc_public_inputs: [Field; C6_FOLD_PUBLIC_LEN], + inner_key_hash: pub Field, + acc_key_hash: pub Field, + is_first_step: pub bool, + slot_index: pub u32, +) -> pub ([Field; T + 1], [Field; T + 1], [Field; T + 1], [Field; T + 1]) { + assert(slot_index < T + 1); + + verify_honk_proof(inner_vk, inner_proof, c6_public_inputs, inner_key_hash); + + // Signature parity with `c6_fold`: see `c3_fold_kernel`. + let _ = (acc_vk, acc_proof, acc_key_hash, acc_public_inputs); + + assert(is_first_step); + + let sk_new = c6_public_inputs[0]; + let esm_new = c6_public_inputs[1]; + let ct_new = c6_public_inputs[2]; + let d_new = c6_public_inputs[3]; + + let mut out_sk: [Field; T + 1] = [0; T + 1]; + let mut out_esm: [Field; T + 1] = [0; T + 1]; + let mut out_ct: [Field; T + 1] = [0; T + 1]; + let mut out_d: [Field; T + 1] = [0; T + 1]; + + for i in 0..(T + 1) { + let idx = i as u32; + if idx == slot_index { + out_sk[i] = sk_new; + out_esm[i] = esm_new; + out_ct[i] = ct_new; + out_d[i] = d_new; + } else { + out_sk[i] = 0; + out_esm[i] = 0; + out_ct[i] = 0; + out_d[i] = 0; + } + } + + (out_sk, out_esm, out_ct, out_d) +} diff --git a/circuits/bin/recursive_aggregation/decryption_aggregator/Nargo.toml b/circuits/bin/recursive_aggregation/decryption_aggregator/Nargo.toml new file mode 100644 index 0000000000..e0a53622e0 --- /dev/null +++ b/circuits/bin/recursive_aggregation/decryption_aggregator/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "decryption_aggregator" +type = "bin" +authors = ["Gnosis Guild / Enclave"] + +[dependencies] +lib = { path = "../../../lib" } +bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr b/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr new file mode 100644 index 0000000000..7bd53501f1 --- /dev/null +++ b/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Decryption aggregator: non-ZK `c6_fold` (folded C6 column) + non-ZK `decrypted_shares_aggregation` +//! (C7, both `noir-recursive-no-zk`). The final `bb prove -t evm` proof of this circuit is what the +//! contract verifies on-chain. + +use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use lib::configs::default::MAX_MSG_NON_ZERO_COEFFS; +use lib::configs::default::T; +use lib::math::commitments::compute_vk_hash; + +/// Must match `c6_fold::C6_FOLD_PUBLIC_LEN`: pub params + flattened `(out_sk, out_esm, out_ct, out_d)`. +pub global C6_FOLD_PUBLIC_LEN: u32 = 4 + (4 * (T + 1)); + +pub global C6_FOLD_PREFIX_LEN: u32 = C6_FOLD_PUBLIC_LEN - (4 * (T + 1)); + +/// Column offsets inside the `c6_fold` flattened tail (after `C6_FOLD_PREFIX_LEN`): +/// `(out_sk, out_esm, out_ct, out_d)`, each `C6_FOLD_COL_WIDTH = T + 1` long. +pub global C6_FOLD_COL_WIDTH: u32 = T + 1; +pub global C6_FOLD_SK_COL_OFFSET: u32 = 0; +pub global C6_FOLD_ESM_COL_OFFSET: u32 = C6_FOLD_COL_WIDTH; +pub global C6_FOLD_CT_COL_OFFSET: u32 = 2 * C6_FOLD_COL_WIDTH; +pub global C6_FOLD_D_COL_OFFSET: u32 = 3 * C6_FOLD_COL_WIDTH; + +/// `c7_public` layout: `[expected_d_commitments | party_ids | message]`. +pub global C7_D_COMMITMENT_OFFSET: u32 = 0; +pub global C7_PARTY_ID_OFFSET: u32 = T + 1; +pub global C7_MESSAGE_OFFSET: u32 = 2 * (T + 1); + +pub global C7_PUBLIC_LEN: u32 = (2 * (T + 1)) + MAX_MSG_NON_ZERO_COEFFS; + +fn main( + c6_fold_vk: UltraHonkVerificationKey, + c6_fold_proof: UltraHonkProof, + c6_fold_public: [Field; C6_FOLD_PUBLIC_LEN], + c7_vk: UltraHonkVerificationKey, + c7_proof: UltraHonkProof, + c7_public: [Field; C7_PUBLIC_LEN], + c6_fold_key_hash: pub Field, + c7_key_hash: pub Field, +) -> pub (Field, [Field; T + 1], [Field; T + 1], [Field; T + 1], [Field; MAX_MSG_NON_ZERO_COEFFS]) { + verify_honk_proof_non_zk(c6_fold_vk, c6_fold_proof, c6_fold_public, c6_fold_key_hash); + verify_honk_proof_non_zk(c7_vk, c7_proof, c7_public, c7_key_hash); + + // Cross-circuit C6 to C7: each folded `d_commitment` column entry must match `c7_public[i]` + // (leading `T + 1` words of `c7_public`). + for i in 0..(T + 1) { + assert( + c6_fold_public[C6_FOLD_PREFIX_LEN + C6_FOLD_D_COL_OFFSET + i] + == c7_public[C7_D_COMMITMENT_OFFSET + i], + ); + } + + let mut vk_hashes = Vec::new(); + vk_hashes.push(c6_fold_key_hash); + vk_hashes.push(c7_key_hash); + let key_hash = compute_vk_hash(vk_hashes); + + let mut party_ids: [Field; T + 1] = [0; T + 1]; + let mut expected_sk: [Field; T + 1] = [0; T + 1]; + let mut expected_esm: [Field; T + 1] = [0; T + 1]; + for i in 0..(T + 1) { + party_ids[i] = c7_public[C7_PARTY_ID_OFFSET + i]; + expected_sk[i] = c6_fold_public[C6_FOLD_PREFIX_LEN + C6_FOLD_SK_COL_OFFSET + i]; + expected_esm[i] = c6_fold_public[C6_FOLD_PREFIX_LEN + C6_FOLD_ESM_COL_OFFSET + i]; + } + + let mut message: [Field; MAX_MSG_NON_ZERO_COEFFS] = [0; MAX_MSG_NON_ZERO_COEFFS]; + for k in 0..MAX_MSG_NON_ZERO_COEFFS { + message[k] = c7_public[C7_MESSAGE_OFFSET + k]; + } + + (key_hash, party_ids, expected_sk, expected_esm, message) +} diff --git a/circuits/bin/recursive_aggregation/dkg_aggregator/Nargo.toml b/circuits/bin/recursive_aggregation/dkg_aggregator/Nargo.toml new file mode 100644 index 0000000000..be887d7962 --- /dev/null +++ b/circuits/bin/recursive_aggregation/dkg_aggregator/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "dkg_aggregator" +type = "bin" +authors = ["Gnosis Guild / Enclave"] + +[dependencies] +lib = { path = "../../../lib" } +bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr b/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr new file mode 100644 index 0000000000..f82707dbe7 --- /dev/null +++ b/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! DKG aggregator: non-ZK `nodes_fold` and non-ZK `pk_aggregation` (both `noir-recursive-no-zk`). +//! The final `bb prove -t evm` proof of this circuit is what the contract verifies on-chain. + +use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use lib::configs::default::dkg::L_THRESHOLD; +use lib::configs::default::{H, N_PARTIES}; +use lib::math::commitments::compute_vk_hash; + +/// Must match `node_fold::NODE_FOLD_PUBLIC_LEN`. +pub global NODE_FOLD_PUBLIC_LEN: u32 = 11 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); + +/// Must match `nodes_fold::NODES_FOLD_PUBLIC_LEN` (pub params + flattened `H` node-fold rows). +pub global NODES_FOLD_PUBLIC_LEN: u32 = 4 + (H * NODE_FOLD_PUBLIC_LEN); + +/// Flattened `node_fold` return tuple length (key hash + pks + share grids + agg commits). +pub global NODE_FOLD_RETURN_PUBLIC_LEN: u32 = + 5 + N_PARTIES + (2 * N_PARTIES * L_THRESHOLD) + (2 * H * L_THRESHOLD); + +/// Leading `node_fold` public inputs (party id + five key hashes) before the return tuple. +pub global NODE_FOLD_PARAM_PUBLIC_LEN: u32 = NODE_FOLD_PUBLIC_LEN - NODE_FOLD_RETURN_PUBLIC_LEN; + +/// Leading `nodes_fold` public inputs before the `H` full `node_fold` rows. +pub global NODES_FOLD_PARAM_PUBLIC_LEN: u32 = NODES_FOLD_PUBLIC_LEN - (H * NODE_FOLD_PUBLIC_LEN); + +/// Indices within one `node_fold` public row (length `NODE_FOLD_PUBLIC_LEN`). +pub global NF_PARTY_ID_IDX: u32 = 0; +pub global NF_C0_PK_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 1; +pub global NF_C1_PK_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 2; +pub global NF_C3_PK_BASE_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 3; +pub global NF_C2A_BASE_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 3 + N_PARTIES; +pub global NF_C2B_BASE_IDX: u32 = NF_C2A_BASE_IDX + (N_PARTIES * L_THRESHOLD); +pub global NF_C4A_BASE_IDX: u32 = NF_C2B_BASE_IDX + (N_PARTIES * L_THRESHOLD); +pub global NF_C4B_BASE_IDX: u32 = NF_C4A_BASE_IDX + (H * L_THRESHOLD); +pub global NF_SK_AGG_IDX: u32 = NF_C4B_BASE_IDX + (H * L_THRESHOLD); +pub global NF_ESM_AGG_IDX: u32 = NF_SK_AGG_IDX + 1; + +fn main( + nodes_fold_vk: UltraHonkVerificationKey, + nodes_fold_proof: UltraHonkProof, + nodes_fold_public: [Field; NODES_FOLD_PUBLIC_LEN], + c5_vk: UltraHonkVerificationKey, + c5_proof: UltraHonkProof, + c5_public: [Field; H + 1], + nodes_fold_key_hash: pub Field, + c5_key_hash: pub Field, + party_ids: pub [Field; H], +) -> pub (Field, [Field; H], [Field; H], Field) { + verify_honk_proof_non_zk( + nodes_fold_vk, + nodes_fold_proof, + nodes_fold_public, + nodes_fold_key_hash, + ); + verify_honk_proof_non_zk(c5_vk, c5_proof, c5_public, c5_key_hash); + + // Bind each folded `node_fold` row to the aggregator-supplied honest-set ordering: slot 0 per row + // is the party id (matches `party_ids[i]`). + for i in 0..H { + assert( + nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + + i * NODE_FOLD_PUBLIC_LEN + + NF_PARTY_ID_IDX] + == party_ids[i], + ); + } + + // Cross-node C0 to C3: each row's c3_pk[j] (recipient pk used in party i's C3 encryption for + // recipient j) must equal party j's own c0_pk. + for i in 0..H { + for j in 0..H { + assert( + nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + + i * NODE_FOLD_PUBLIC_LEN + + NF_C3_PK_BASE_IDX + + j] + == nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + + j * NODE_FOLD_PUBLIC_LEN + + NF_C0_PK_IDX], + ); + } + } + + // Cross-party C2 to C4 (first chain). + for i in 0..H { + for j in 0..H { + for l in 0..L_THRESHOLD { + assert( + nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + + j * NODE_FOLD_PUBLIC_LEN + + NF_C4A_BASE_IDX + + i * L_THRESHOLD + + l] + == nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + + i * NODE_FOLD_PUBLIC_LEN + + NF_C2A_BASE_IDX + + j * L_THRESHOLD + + l], + ); + } + } + } + + // Cross-party C2 to C4 (second chain). + for i in 0..H { + for j in 0..H { + for l in 0..L_THRESHOLD { + assert( + nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + + j * NODE_FOLD_PUBLIC_LEN + + NF_C4B_BASE_IDX + + i * L_THRESHOLD + + l] + == nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + + i * NODE_FOLD_PUBLIC_LEN + + NF_C2B_BASE_IDX + + j * L_THRESHOLD + + l], + ); + } + } + } + + // Cross-circuit C5 to `node_fold`: C5 row `i` matches that node's `c1_pk` public field. + for i in 0..H { + assert( + c5_public[i] + == nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + + i * NODE_FOLD_PUBLIC_LEN + + NF_C1_PK_IDX], + ); + } + + let mut vk_hashes = Vec::new(); + vk_hashes.push(nodes_fold_key_hash); + vk_hashes.push(c5_key_hash); + let key_hash = compute_vk_hash(vk_hashes); + + let mut sk_agg_commits: [Field; H] = [0; H]; + let mut esm_agg_commits: [Field; H] = [0; H]; + for i in 0..H { + sk_agg_commits[i] = nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + + i * NODE_FOLD_PUBLIC_LEN + + NF_SK_AGG_IDX]; + esm_agg_commits[i] = nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + + i * NODE_FOLD_PUBLIC_LEN + + NF_ESM_AGG_IDX]; + } + let aggregated_pk_commit = c5_public[H]; + + (key_hash, sk_agg_commits, esm_agg_commits, aggregated_pk_commit) +} diff --git a/circuits/bin/recursive_aggregation/fold/README.md b/circuits/bin/recursive_aggregation/fold/README.md deleted file mode 100644 index 5ce4c28170..0000000000 --- a/circuits/bin/recursive_aggregation/fold/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# `fold` - -Pairwise aggregation of **two** non-ZK UltraHonk proofs: verifies each under its VK, merges -commitments, and computes a **genealogy** `key_hash` over inner key hashes and VK hashes. - -| | | -| ------------ | ----------------------------------------------------------------- | -| **Source** | [`src/main.nr`](src/main.nr) | -| **Wrappers** | [../wrapper/README.md](../wrapper/README.md) | -| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | -| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | - -Output: `pub (Field, Field)` = `(key_hash, commitment)`. diff --git a/circuits/bin/recursive_aggregation/fold/src/main.nr b/circuits/bin/recursive_aggregation/fold/src/main.nr deleted file mode 100644 index 3f79183aa7..0000000000 --- a/circuits/bin/recursive_aggregation/fold/src/main.nr +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; -use lib::math::commitments::{compute_recursive_aggregation_commitment, compute_vk_hash}; - -fn main( - proof1_verification_key: UltraHonkVerificationKey, - proof1_proof: UltraHonkProof, - proof1_public_inputs: [Field; 2], - proof1_key_hash: Field, - proof2_verification_key: UltraHonkVerificationKey, - proof2_proof: UltraHonkProof, - proof2_public_inputs: [Field; 2], - proof2_key_hash: Field, -) -> pub (Field, Field) { - verify_honk_proof_non_zk( - proof1_verification_key, - proof1_proof, - proof1_public_inputs, - proof1_key_hash, - ); - verify_honk_proof_non_zk( - proof2_verification_key, - proof2_proof, - proof2_public_inputs, - proof2_key_hash, - ); - - // Combine the two inner commitments with SAFE (`compute_recursive_aggregation_commitment`). - let mut commitments_vec = Vec::new(); - commitments_vec.push(proof1_public_inputs[1]); - commitments_vec.push(proof2_public_inputs[1]); - - let commitment = compute_recursive_aggregation_commitment(commitments_vec); - - // Hash the full VK chain: attested key hashes from inner proofs (public_inputs[0]) plus the VKs - // that verified them (proof*_key_hash). This combined fingerprint lets the verifier check the - // entire proof genealogy: which circuits were folded and which verified each level. - let mut vk_hashes = Vec::new(); - vk_hashes.push(proof1_public_inputs[0]); // key_hash attested by proof1 (from its inner folds) - vk_hashes.push(proof2_public_inputs[0]); // key_hash attested by proof2 (from its inner folds) - vk_hashes.push(proof1_key_hash); // VK hash of circuit that produced proof1 - vk_hashes.push(proof2_key_hash); // VK hash of circuit that produced proof2 - let key_hash = compute_vk_hash(vk_hashes); - - (key_hash, commitment) -} diff --git a/circuits/bin/recursive_aggregation/node_fold/Nargo.toml b/circuits/bin/recursive_aggregation/node_fold/Nargo.toml new file mode 100644 index 0000000000..94c75f7021 --- /dev/null +++ b/circuits/bin/recursive_aggregation/node_fold/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "node_fold" +type = "bin" +authors = ["Gnosis Guild / Enclave"] + +[dependencies] +lib = { path = "../../../lib" } +bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/node_fold/src/main.nr b/circuits/bin/recursive_aggregation/node_fold/src/main.nr new file mode 100644 index 0000000000..b52d3a66ac --- /dev/null +++ b/circuits/bin/recursive_aggregation/node_fold/src/main.nr @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Per-node fold: C0, C1, `c2ab_fold`, `c3ab_fold`, `c4ab_fold` with same-party assertions. + +use bb_proof_verification::{ + UltraHonkProof, UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof, + verify_honk_proof_non_zk, +}; +use lib::configs::default::dkg::L_THRESHOLD; +use lib::configs::default::{H, N_PARTIES}; +use lib::math::commitments::compute_vk_hash; + +pub global C2_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); +pub global C2AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C2_PUBLIC_LEN); +/// One slot per (party, threshold-modulus) pair - must match `c3ab_fold::C3AB_FOLD_PUBLIC_LEN`. +pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD; +pub global C3AB_FOLD_PUBLIC_LEN: u32 = 2 + 1 + (6 * C3_SLOTS); +/// Prefix (two inner key hashes + combined hash) before six `C3_SLOTS` columns in `c3ab_fold` public IO. +pub global C3AB_PREFIX_LEN: u32 = C3AB_FOLD_PUBLIC_LEN - (6 * C3_SLOTS); +pub global C4_PUBLIC_LEN: u32 = (H * L_THRESHOLD) + 1; +pub global C4AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C4_PUBLIC_LEN); + +/// Key-hash prefix length in `c2ab_fold` / `c4ab_fold` flattened public IO. +pub global C2AB_PREFIX_LEN: u32 = C2AB_FOLD_PUBLIC_LEN - (2 * C2_PUBLIC_LEN); +pub global C4AB_PREFIX_LEN: u32 = C4AB_FOLD_PUBLIC_LEN - (2 * C4_PUBLIC_LEN); + +/// Inner C2 / C4 column offsets inside `c2ab_public` / `c4ab_public` +/// (parallel to `NF_*` in `dkg_aggregator`; keep in sync). +pub global C2A_INNER_START: u32 = C2AB_PREFIX_LEN; +pub global C2B_INNER_START: u32 = C2AB_PREFIX_LEN + C2_PUBLIC_LEN; +pub global C2A_SHARES_START: u32 = C2A_INNER_START + 1; +pub global C2B_SHARES_START: u32 = C2B_INNER_START + 1; + +pub global C4A_INNER_START: u32 = C4AB_PREFIX_LEN; +pub global C4B_INNER_START: u32 = C4AB_PREFIX_LEN + C4_PUBLIC_LEN; +pub global SK_AGG_INDEX: u32 = C4A_INNER_START + (H * L_THRESHOLD); +pub global ESM_AGG_INDEX: u32 = C4B_INNER_START + (H * L_THRESHOLD); + +/// `c3ab_fold` flattened tail: pk_a, msg_a, ct_a, pk_b, msg_b, ct_b - each `C3_SLOTS` long. +pub global C3AB_PK_A_START: u32 = C3AB_PREFIX_LEN; +pub global C3AB_MSG_A_START: u32 = C3AB_PREFIX_LEN + C3_SLOTS; +pub global C3AB_PK_B_START: u32 = C3AB_PREFIX_LEN + (3 * C3_SLOTS); +pub global C3AB_MSG_B_START: u32 = C3AB_PREFIX_LEN + (4 * C3_SLOTS); + +/// Total public parameters + outputs for `node_fold`. +pub global NODE_FOLD_PUBLIC_LEN: u32 = 11 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); + +fn main( + c0_vk: UltraHonkVerificationKey, + c0_proof: UltraHonkZKProof, + c0_public: [Field; 1], + c1_vk: UltraHonkVerificationKey, + c1_proof: UltraHonkZKProof, + c1_public: [Field; 3], + c2ab_vk: UltraHonkVerificationKey, + c2ab_proof: UltraHonkProof, + c2ab_public: [Field; C2AB_FOLD_PUBLIC_LEN], + c3ab_vk: UltraHonkVerificationKey, + c3ab_proof: UltraHonkProof, + c3ab_public: [Field; C3AB_FOLD_PUBLIC_LEN], + c4ab_vk: UltraHonkVerificationKey, + c4ab_proof: UltraHonkProof, + c4ab_public: [Field; C4AB_FOLD_PUBLIC_LEN], + // Bound to sortition slot via `NF_PARTY_ID_IDX` in `dkg_aggregator`; do not rename. + party_id: pub Field, + c0_key_hash: pub Field, + c1_key_hash: pub Field, + c2ab_key_hash: pub Field, + c3ab_key_hash: pub Field, + c4ab_key_hash: pub Field, + ) -> pub (Field, Field, Field, [Field; N_PARTIES], [[Field; L_THRESHOLD]; N_PARTIES], [[Field; L_THRESHOLD]; N_PARTIES], [[Field; L_THRESHOLD]; H], [[Field; L_THRESHOLD]; H], Field, Field) { + verify_honk_proof(c0_vk, c0_proof, c0_public, c0_key_hash); + verify_honk_proof(c1_vk, c1_proof, c1_public, c1_key_hash); + verify_honk_proof_non_zk(c2ab_vk, c2ab_proof, c2ab_public, c2ab_key_hash); + verify_honk_proof_non_zk(c3ab_vk, c3ab_proof, c3ab_public, c3ab_key_hash); + verify_honk_proof_non_zk(c4ab_vk, c4ab_proof, c4ab_public, c4ab_key_hash); + + let mut vk_hashes = Vec::new(); + vk_hashes.push(c0_key_hash); + vk_hashes.push(c1_key_hash); + vk_hashes.push(c2ab_key_hash); + vk_hashes.push(c3ab_key_hash); + vk_hashes.push(c4ab_key_hash); + let key_hash = compute_vk_hash(vk_hashes); + + // C0: `commit(pk_dkg)` (DKG key for C3). C1[1]: `commit(pk_trbfv)` (threshold key); not equal to C0. + let c0_pk = c0_public[0]; + let c1_pk = c1_public[1]; + + // Same-party C1 to C2: `c2ab_public` lays out two full C2 public vectors after a 3-field + // prefix (`C2AB_FOLD_PUBLIC_LEN` = 3 + 2 * `C2_PUBLIC_LEN`). For each inner C2 block, index 0 + // is `expected_secret_commitment`; these equal C1's sk- and e_sm-side public inputs [0] and [2]. + assert(c1_public[0] == c2ab_public[C2A_INNER_START]); + assert(c1_public[2] == c2ab_public[C2B_INNER_START]); + + // Same-party C3a and C3b: pk_a[j*L+l] and pk_b[j*L+l] must match for every (party, modulus) + // slot - recipient public key must be consistent across both folded C3 chains. + for j in 0..N_PARTIES { + for l in 0..L_THRESHOLD { + let slot = j * L_THRESHOLD + l; + assert(c3ab_public[C3AB_PK_A_START + slot] == c3ab_public[C3AB_PK_B_START + slot]); + } + } + + // C2 to C3: all (party, modulus) slots must be bound - msg_a[j*L+l] == c2a_shares[j][l] + // and msg_b[j*L+l] == c2b_shares[j][l]. + // c3ab layout: key-hash prefix, then pk_a, msg_a, ct_a, pk_b, msg_b, ct_b (`C3_SLOTS` each). + // c2ab layout: see `C2AB_PREFIX_LEN` / `C2_PUBLIC_LEN`. + for j in 0..N_PARTIES { + for l in 0..L_THRESHOLD { + let slot = j * L_THRESHOLD + l; + assert(c2ab_public[C2A_SHARES_START + slot] == c3ab_public[C3AB_MSG_A_START + slot]); + assert(c2ab_public[C2B_SHARES_START + slot] == c3ab_public[C3AB_MSG_B_START + slot]); + } + } + + let mut c3_pk: [Field; N_PARTIES] = [0; N_PARTIES]; + for j in 0..N_PARTIES { + // pk is per-party (same DKG key across all moduli of the same recipient): use slot j*L+0. + c3_pk[j] = c3ab_public[C3AB_PK_A_START + j * L_THRESHOLD]; + } + + let mut c2a: [[Field; L_THRESHOLD]; N_PARTIES] = [[0; L_THRESHOLD]; N_PARTIES]; + let mut c2b: [[Field; L_THRESHOLD]; N_PARTIES] = [[0; L_THRESHOLD]; N_PARTIES]; + for j in 0..N_PARTIES { + for l in 0..L_THRESHOLD { + c2a[j][l] = c2ab_public[C2A_SHARES_START + j * L_THRESHOLD + l]; + c2b[j][l] = c2ab_public[C2B_SHARES_START + j * L_THRESHOLD + l]; + } + } + + let mut c4a_exp: [[Field; L_THRESHOLD]; H] = [[0; L_THRESHOLD]; H]; + let mut c4b_exp: [[Field; L_THRESHOLD]; H] = [[0; L_THRESHOLD]; H]; + for h in 0..H { + for l in 0..L_THRESHOLD { + c4a_exp[h][l] = c4ab_public[C4A_INNER_START + h * L_THRESHOLD + l]; + c4b_exp[h][l] = c4ab_public[C4B_INNER_START + h * L_THRESHOLD + l]; + } + } + let sk_agg = c4ab_public[SK_AGG_INDEX]; + let esm_agg = c4ab_public[ESM_AGG_INDEX]; + + (key_hash, c0_pk, c1_pk, c3_pk, c2a, c2b, c4a_exp, c4b_exp, sk_agg, esm_agg) +} diff --git a/circuits/bin/recursive_aggregation/nodes_fold/Nargo.toml b/circuits/bin/recursive_aggregation/nodes_fold/Nargo.toml new file mode 100644 index 0000000000..7f441ed4d6 --- /dev/null +++ b/circuits/bin/recursive_aggregation/nodes_fold/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "nodes_fold" +type = "bin" +authors = ["Gnosis Guild / Enclave"] + +[dependencies] +lib = { path = "../../../lib" } +bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/nodes_fold/src/main.nr b/circuits/bin/recursive_aggregation/nodes_fold/src/main.nr new file mode 100644 index 0000000000..60599b3bcd --- /dev/null +++ b/circuits/bin/recursive_aggregation/nodes_fold/src/main.nr @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Sequential fold of `H` per-node `node_fold` proofs (non-ZK), one honest slot per step, chained with +//! the previous `nodes_fold` non-ZK proof, or a fixed genesis proof from `nodes_fold_kernel` on the +//! first step (see `crates/zk-prover` and `nodes_fold_kernel`). + +use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use lib::configs::default::dkg::L_THRESHOLD; +use lib::configs::default::{H, N_PARTIES}; + +pub global C2_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); +pub global C2AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C2_PUBLIC_LEN); +/// Must match `c3ab_fold::C3AB_FOLD_PUBLIC_LEN` (one C3 slot per party and CRT limb). +pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD; +pub global C3AB_FOLD_PUBLIC_LEN: u32 = 2 + 1 + (6 * C3_SLOTS); +pub global C4_PUBLIC_LEN: u32 = (H * L_THRESHOLD) + 1; +pub global C4AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C4_PUBLIC_LEN); + +/// Must match `node_fold::NODE_FOLD_PUBLIC_LEN`. +pub global NODE_FOLD_PUBLIC_LEN: u32 = 11 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); + +/// Same shape as `c6_fold`: 4 pub parameters + `H` full `node_fold` public rows (flattened). +pub global NODES_FOLD_PUBLIC_LEN: u32 = 4 + (H * NODE_FOLD_PUBLIC_LEN); + +pub global NODES_FOLD_PARAM_PUBLIC_LEN: u32 = NODES_FOLD_PUBLIC_LEN - (H * NODE_FOLD_PUBLIC_LEN); + +fn main( + inner_vk: UltraHonkVerificationKey, + inner_proof: UltraHonkProof, + node_fold_public_inputs: [Field; NODE_FOLD_PUBLIC_LEN], + acc_vk: UltraHonkVerificationKey, + acc_proof: UltraHonkProof, + acc_public_inputs: [Field; NODES_FOLD_PUBLIC_LEN], + inner_key_hash: pub Field, + acc_key_hash: pub Field, + is_first_step: pub bool, + slot_index: pub u32, +) -> pub [[Field; NODE_FOLD_PUBLIC_LEN]; H] { + assert(slot_index < H); + + verify_honk_proof_non_zk( + inner_vk, + inner_proof, + node_fold_public_inputs, + inner_key_hash, + ); + + verify_honk_proof_non_zk(acc_vk, acc_proof, acc_public_inputs, acc_key_hash); + + let mut out: [[Field; NODE_FOLD_PUBLIC_LEN]; H] = [[0; NODE_FOLD_PUBLIC_LEN]; H]; + + for i in 0..H { + let idx = i as u32; + if is_first_step { + if idx == slot_index { + out[i] = node_fold_public_inputs; + } + } else if idx == slot_index { + for k in 0..NODE_FOLD_PUBLIC_LEN { + assert( + acc_public_inputs[NODES_FOLD_PARAM_PUBLIC_LEN + i * NODE_FOLD_PUBLIC_LEN + k] + == 0, + ); + } + out[i] = node_fold_public_inputs; + } else { + for k in 0..NODE_FOLD_PUBLIC_LEN { + out[i][k] = + acc_public_inputs[NODES_FOLD_PARAM_PUBLIC_LEN + i * NODE_FOLD_PUBLIC_LEN + k]; + } + } + } + + out +} diff --git a/circuits/bin/recursive_aggregation/nodes_fold_kernel/Nargo.toml b/circuits/bin/recursive_aggregation/nodes_fold_kernel/Nargo.toml new file mode 100644 index 0000000000..cf5528e704 --- /dev/null +++ b/circuits/bin/recursive_aggregation/nodes_fold_kernel/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "nodes_fold_kernel" +type = "bin" +authors = ["Gnosis Guild / Enclave"] + +[dependencies] +lib = { path = "../../../lib" } +bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/nodes_fold_kernel/src/main.nr b/circuits/bin/recursive_aggregation/nodes_fold_kernel/src/main.nr new file mode 100644 index 0000000000..adbda9b37f --- /dev/null +++ b/circuits/bin/recursive_aggregation/nodes_fold_kernel/src/main.nr @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Same IO as [`nodes_fold`](../nodes_fold/src/main.nr) but **only** verifies the inner `node_fold` +//! proof. Used to mint a fixed genesis `UltraHonkProof` that bootstraps the real `nodes_fold` +//! circuit, which always runs `verify_honk_proof_non_zk` on the accumulator (see `nodes_fold`). + +use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use lib::configs::default::dkg::L_THRESHOLD; +use lib::configs::default::{H, N_PARTIES}; + +pub global C2_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); +pub global C2AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C2_PUBLIC_LEN); +/// Must match `c3ab_fold::C3AB_FOLD_PUBLIC_LEN` (one C3 slot per party and CRT limb). +pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD; +pub global C3AB_FOLD_PUBLIC_LEN: u32 = 2 + 1 + (6 * C3_SLOTS); +pub global C4_PUBLIC_LEN: u32 = (H * L_THRESHOLD) + 1; +pub global C4AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C4_PUBLIC_LEN); + +/// Must match `node_fold::NODE_FOLD_PUBLIC_LEN`. +pub global NODE_FOLD_PUBLIC_LEN: u32 = 11 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); + +/// Same shape as `nodes_fold`: 4 pub parameters + `H` full `node_fold` public rows (flattened). +pub global NODES_FOLD_PUBLIC_LEN: u32 = 4 + (H * NODE_FOLD_PUBLIC_LEN); + +fn main( + inner_vk: UltraHonkVerificationKey, + inner_proof: UltraHonkProof, + node_fold_public_inputs: [Field; NODE_FOLD_PUBLIC_LEN], + acc_vk: UltraHonkVerificationKey, + acc_proof: UltraHonkProof, + acc_public_inputs: [Field; NODES_FOLD_PUBLIC_LEN], + inner_key_hash: pub Field, + acc_key_hash: pub Field, + is_first_step: pub bool, + slot_index: pub u32, +) -> pub [[Field; NODE_FOLD_PUBLIC_LEN]; H] { + assert(slot_index < H); + + verify_honk_proof_non_zk( + inner_vk, + inner_proof, + node_fold_public_inputs, + inner_key_hash, + ); + + // Signature parity with `nodes_fold`: see `c3_fold_kernel`. + let _ = (acc_vk, acc_proof, acc_key_hash, acc_public_inputs); + + assert(is_first_step); + + let mut out: [[Field; NODE_FOLD_PUBLIC_LEN]; H] = [[0; NODE_FOLD_PUBLIC_LEN]; H]; + + for i in 0..H { + let idx = i as u32; + if idx == slot_index { + out[i] = node_fold_public_inputs; + } + } + + out +} diff --git a/circuits/bin/recursive_aggregation/wrapper/README.md b/circuits/bin/recursive_aggregation/wrapper/README.md deleted file mode 100644 index 1fc4c80ed3..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Wrapper circuits - -Re-verify **UltraHonk** proofs inside Noir (`verify_honk_proof` / `verify_honk_proof_non_zk`) and -compress public inputs to a **recursive aggregation commitment** (and usually a **key hash** chain) -for folding or cheap verification. - -## Dimensions - -Each subdirectory under `wrapper/dkg/` and `wrapper/threshold/` sets `N_PROOFS` and -`N_PUBLIC_INPUTS` in its `src/main.nr`. Values below match the sources as of this tree; symbols come -from `lib::configs::default` (`L`, `N`, `H`, `T`, `L_THRESHOLD`, `MAX_MSG_NON_ZERO_COEFFS`, etc.). - -| Wrapper path | `N_PROOFS` | `N_PUBLIC_INPUTS` (per proof) | -| ---------------------------------------- | ---------- | ------------------------------------------------------------------------------------------- | -| `dkg/pk` | 1 | `1` | -| `dkg/share_computation` | 1 | `3` (key-hash chain fields + inner proof’s public outputs; **one** inner C2 proof per wrap) | -| `dkg/share_encryption` | 1 | `(2 × L × N) + 2` | -| `dkg/share_decryption` | 1 | `(H × L_THRESHOLD) + 1` | -| `threshold/pk_generation` | 1 | `3` (`sk_commitment`, `pk_commitment`, `e_sm_commitment`) | -| `threshold/pk_aggregation` | 1 | `H + 1` | -| `threshold/share_decryption` | 1 | `2 + 2 × L × N + 1` | -| `threshold/decrypted_shares_aggregation` | 1 | `(T + 1) + MAX_MSG_NON_ZERO_COEFFS + (T + 1)` | - -### P3 user encryption (different path) - -The user-encryption wrapper (ct0 + ct1, shared `u_commitment`) is **not** under -`recursive_aggregation/wrapper/`. It lives at -[`bin/threshold/user_data_encryption`](../../threshold/user_data_encryption): two inner proofs with -**4** public inputs (ct0) and **3** (ct1), `verify_honk_proof_non_zk`, and a **three-field** public -return tuple. See that crate’s `src/main.nr`. - -## Flow - -```mermaid -flowchart LR - Base["base UltraHonk proof"] --> W["wrapper"] - W --> Fold["fold"] -``` - -| | | -| --------------- | --------------------------------------------------------------------- | -| **Fold** | [../fold/README.md](../fold/README.md) | -| **Commitments** | [`lib/src/math/commitments.nr`](../../../lib/src/math/commitments.nr) | -| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | -| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/Nargo.toml b/circuits/bin/recursive_aggregation/wrapper/dkg/Nargo.toml deleted file mode 100644 index 29f0bd03c9..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[workspace] -members = [ - "pk", - "share_computation", - "share_encryption", - "share_decryption", -] \ No newline at end of file diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/pk/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/dkg/pk/src/main.nr deleted file mode 100644 index 9797f1246a..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/pk/src/main.nr +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; -use lib::math::commitments::compute_recursive_aggregation_commitment; - -// Number of proofs. -pub global N_PROOFS: u32 = 1; -/// Number of public inputs/outputs per proof. -pub global N_PUBLIC_INPUTS: u32 = 1; - -fn main( - verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkZKProof; N_PROOFS], - public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], - key_hash: pub Field, -) -> pub Field { - for i in 0..N_PROOFS { - verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); - } - - let mut aggregated_public_inputs = Vec::new(); - - for i in 0..N_PROOFS { - for j in 0..N_PUBLIC_INPUTS { - aggregated_public_inputs.push(public_inputs[i][j]); - } - } - - compute_recursive_aggregation_commitment(aggregated_public_inputs) -} diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/Nargo.toml b/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/Nargo.toml deleted file mode 100644 index 21687b2657..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/Nargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "share_computation" -type = "bin" -authors = ["Gnosis Guild / Enclave"] - -[dependencies] -lib = { path = "../../../../../lib" } -bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/src/main.nr deleted file mode 100644 index 3d33e6f23f..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/src/main.nr +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; -use lib::{ - configs::default::{dkg::L_THRESHOLD, N_PARTIES}, - math::commitments::compute_recursive_aggregation_commitment, -}; - -// Each SK/ESM final C2 proof is wrapped individually after the base -> chunk -> batch pipeline. -pub global N_PROOFS: u32 = 1; -// The share_computation wrapper exposes per-party share commitments plus key hash: -// (L_THRESHOLD * N_PARTIES) commitment fields + 1 key_hash field. -pub global N_PUBLIC_INPUTS: u32 = (L_THRESHOLD * N_PARTIES) + 1; - -fn main( - verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkZKProof; N_PROOFS], - public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], - key_hash: pub Field, -) -> pub Field { - for i in 0..N_PROOFS { - verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); - } - - let mut aggregated_public_inputs = Vec::new(); - - for i in 0..N_PROOFS { - for j in 0..N_PUBLIC_INPUTS { - aggregated_public_inputs.push(public_inputs[i][j]); - } - } - - compute_recursive_aggregation_commitment(aggregated_public_inputs) -} diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/share_decryption/Nargo.toml b/circuits/bin/recursive_aggregation/wrapper/dkg/share_decryption/Nargo.toml deleted file mode 100644 index 425a3dab92..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/share_decryption/Nargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "share_decryption" -type = "bin" -authors = ["Gnosis Guild / Enclave"] - -[dependencies] -lib = { path = "../../../../../lib" } -bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/share_decryption/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/dkg/share_decryption/src/main.nr deleted file mode 100644 index eaa94ca197..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/share_decryption/src/main.nr +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; -use lib::configs::default::dkg::L_THRESHOLD; -use lib::configs::default::H; -use lib::math::commitments::compute_recursive_aggregation_commitment; - -// Number of proofs. -pub global N_PROOFS: u32 = 1; -/// Number of public inputs/outputs per proof. -pub global N_PUBLIC_INPUTS: u32 = (H * L_THRESHOLD) + 1; - -fn main( - verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkZKProof; N_PROOFS], - public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], - key_hash: pub Field, -) -> pub Field { - for i in 0..N_PROOFS { - verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); - } - - let mut aggregated_public_inputs = Vec::new(); - - for i in 0..N_PROOFS { - for j in 0..N_PUBLIC_INPUTS { - aggregated_public_inputs.push(public_inputs[i][j]); - } - } - - compute_recursive_aggregation_commitment(aggregated_public_inputs) -} diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/share_encryption/Nargo.toml b/circuits/bin/recursive_aggregation/wrapper/dkg/share_encryption/Nargo.toml deleted file mode 100644 index 868126a03c..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/share_encryption/Nargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "share_encryption" -type = "bin" -authors = ["Gnosis Guild / Enclave"] - -[dependencies] -lib = { path = "../../../../../lib" } -bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/share_encryption/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/dkg/share_encryption/src/main.nr deleted file mode 100644 index 2d9b92e56d..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/share_encryption/src/main.nr +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; -use lib::{ - configs::default::dkg::{L, N}, - math::commitments::compute_recursive_aggregation_commitment, -}; - -// Number of proofs. -pub global N_PROOFS: u32 = 1; -/// Number of public inputs/outputs per proof. -pub global N_PUBLIC_INPUTS: u32 = (2 * L * N) + 2; - -fn main( - verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkZKProof; N_PROOFS], - public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], - key_hash: pub Field, -) -> pub Field { - for i in 0..N_PROOFS { - verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); - } - - let mut aggregated_public_inputs = Vec::new(); - - for i in 0..N_PROOFS { - for j in 0..N_PUBLIC_INPUTS { - aggregated_public_inputs.push(public_inputs[i][j]); - } - } - - compute_recursive_aggregation_commitment(aggregated_public_inputs) -} diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/Nargo.toml b/circuits/bin/recursive_aggregation/wrapper/threshold/Nargo.toml deleted file mode 100644 index 72a1438e2f..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[workspace] -members = [ - "pk_generation", - "pk_aggregation", - "share_decryption", - "decrypted_shares_aggregation" -] \ No newline at end of file diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/decrypted_shares_aggregation/Nargo.toml b/circuits/bin/recursive_aggregation/wrapper/threshold/decrypted_shares_aggregation/Nargo.toml deleted file mode 100644 index 1c3d7bd63f..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/decrypted_shares_aggregation/Nargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "decrypted_shares_aggregation" -type = "bin" -authors = ["Gnosis Guild / Enclave"] - -[dependencies] -lib = { path = "../../../../../lib" } -bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/decrypted_shares_aggregation/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/threshold/decrypted_shares_aggregation/src/main.nr deleted file mode 100644 index 9119f31a29..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/decrypted_shares_aggregation/src/main.nr +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; -use lib::{ - configs::default::{MAX_MSG_NON_ZERO_COEFFS, T}, - math::commitments::compute_recursive_aggregation_commitment, -}; - -// Number of proofs. -pub global N_PROOFS: u32 = 1; -/// Number of public inputs/outputs per proof. -pub global N_PUBLIC_INPUTS: u32 = (T + 1) + MAX_MSG_NON_ZERO_COEFFS + (T + 1); - -fn main( - verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkZKProof; N_PROOFS], - public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], - key_hash: pub Field, -) -> pub Field { - for i in 0..N_PROOFS { - verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); - } - - let mut aggregated_public_inputs = Vec::new(); - - for i in 0..N_PROOFS { - for j in 0..N_PUBLIC_INPUTS { - aggregated_public_inputs.push(public_inputs[i][j]); - } - } - - compute_recursive_aggregation_commitment(aggregated_public_inputs) -} diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/pk_aggregation/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/threshold/pk_aggregation/src/main.nr deleted file mode 100644 index 030bfaee5c..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/pk_aggregation/src/main.nr +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; -use lib::{configs::default::H, math::commitments::compute_recursive_aggregation_commitment}; - -// Number of proofs. -pub global N_PROOFS: u32 = 1; -/// Number of public inputs/outputs per proof. -pub global N_PUBLIC_INPUTS: u32 = H + 1; - -fn main( - verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkZKProof; N_PROOFS], - public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], - key_hash: pub Field, -) -> pub Field { - for i in 0..N_PROOFS { - verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); - } - - let mut aggregated_public_inputs = Vec::new(); - - for i in 0..N_PROOFS { - for j in 0..N_PUBLIC_INPUTS { - aggregated_public_inputs.push(public_inputs[i][j]); - } - } - - compute_recursive_aggregation_commitment(aggregated_public_inputs) -} diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/pk_generation/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/threshold/pk_generation/src/main.nr deleted file mode 100644 index 76c79b0c21..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/pk_generation/src/main.nr +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; -use lib::math::commitments::compute_recursive_aggregation_commitment; - -// Number of proofs. -pub global N_PROOFS: u32 = 1; -/// Number of public inputs/outputs per proof (sk_commitment, pk_commitment, e_sm_commitment). -pub global N_PUBLIC_INPUTS: u32 = 3; - -fn main( - verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkZKProof; N_PROOFS], - public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], - key_hash: pub Field, -) -> pub Field { - for i in 0..N_PROOFS { - verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); - } - - let mut aggregated_public_inputs = Vec::new(); - - for i in 0..N_PROOFS { - for j in 0..N_PUBLIC_INPUTS { - aggregated_public_inputs.push(public_inputs[i][j]); - } - } - - compute_recursive_aggregation_commitment(aggregated_public_inputs) -} diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/share_decryption/Nargo.toml b/circuits/bin/recursive_aggregation/wrapper/threshold/share_decryption/Nargo.toml deleted file mode 100644 index 425a3dab92..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/share_decryption/Nargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "share_decryption" -type = "bin" -authors = ["Gnosis Guild / Enclave"] - -[dependencies] -lib = { path = "../../../../../lib" } -bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/share_decryption/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/threshold/share_decryption/src/main.nr deleted file mode 100644 index e83aafab3e..0000000000 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/share_decryption/src/main.nr +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; -use lib::configs::default::threshold::{L, N}; -use lib::math::commitments::compute_recursive_aggregation_commitment; - -// Number of proofs. -pub global N_PROOFS: u32 = 1; -/// Number of public inputs/outputs per proof. -pub global N_PUBLIC_INPUTS: u32 = 2 + 2 * L * N + 1; - -fn main( - verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkZKProof; N_PROOFS], - public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], - key_hash: pub Field, -) -> pub Field { - for i in 0..N_PROOFS { - verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); - } - - let mut aggregated_public_inputs = Vec::new(); - - for i in 0..N_PROOFS { - for j in 0..N_PUBLIC_INPUTS { - aggregated_public_inputs.push(public_inputs[i][j]); - } - } - - compute_recursive_aggregation_commitment(aggregated_public_inputs) -} diff --git a/circuits/bin/threshold/decrypted_shares_aggregation/src/main.nr b/circuits/bin/threshold/decrypted_shares_aggregation/src/main.nr index f77cb65ff8..c9e6b13109 100644 --- a/circuits/bin/threshold/decrypted_shares_aggregation/src/main.nr +++ b/circuits/bin/threshold/decrypted_shares_aggregation/src/main.nr @@ -6,7 +6,7 @@ use lib::configs::default::{MAX_MSG_NON_ZERO_COEFFS, T}; use lib::configs::default::threshold::{ - DECRYPTED_SHARES_AGGREGATION_BIT_D, DECRYPTED_SHARES_AGGREGATION_BIT_NOISE, + DECRYPTED_SHARES_AGGREGATION_BIT_D_NATIVE, DECRYPTED_SHARES_AGGREGATION_BIT_NOISE, DECRYPTED_SHARES_AGGREGATION_CONFIGS, L, }; use lib::core::threshold::decrypted_shares_aggregation::DecryptedSharesAggregation; @@ -20,7 +20,7 @@ fn main( u_global: Polynomial, crt_quotients: [Polynomial; L], ) { - let decrypted_shares_aggregation: DecryptedSharesAggregation = DecryptedSharesAggregation::new( + let decrypted_shares_aggregation: DecryptedSharesAggregation = DecryptedSharesAggregation::new( DECRYPTED_SHARES_AGGREGATION_CONFIGS, expected_d_commitments, decryption_shares, diff --git a/circuits/bin/threshold/share_decryption/src/main.nr b/circuits/bin/threshold/share_decryption/src/main.nr index a8c18e015d..da2ad3cace 100644 --- a/circuits/bin/threshold/share_decryption/src/main.nr +++ b/circuits/bin/threshold/share_decryption/src/main.nr @@ -7,9 +7,9 @@ use lib::configs::default::MAX_MSG_NON_ZERO_COEFFS; use lib::configs::default::threshold::{ L, N, THRESHOLD_SHARE_DECRYPTION_BIT_CT, THRESHOLD_SHARE_DECRYPTION_BIT_D, - THRESHOLD_SHARE_DECRYPTION_BIT_E_SM, THRESHOLD_SHARE_DECRYPTION_BIT_R1, - THRESHOLD_SHARE_DECRYPTION_BIT_R2, THRESHOLD_SHARE_DECRYPTION_BIT_SK, - THRESHOLD_SHARE_DECRYPTION_CONFIGS, + THRESHOLD_SHARE_DECRYPTION_BIT_D_NATIVE, THRESHOLD_SHARE_DECRYPTION_BIT_E_SM, + THRESHOLD_SHARE_DECRYPTION_BIT_R1, THRESHOLD_SHARE_DECRYPTION_BIT_R2, + THRESHOLD_SHARE_DECRYPTION_BIT_SK, THRESHOLD_SHARE_DECRYPTION_CONFIGS, }; use lib::core::threshold::share_decryption::ShareDecryption; use lib::math::polynomial::Polynomial; @@ -17,18 +17,21 @@ use lib::math::polynomial::Polynomial; fn main( expected_sk_commitment: pub Field, expected_e_sm_commitment: pub Field, - ct0: pub [Polynomial; L], - ct1: pub [Polynomial; L], + ct_commitment: pub Field, + ct0: [Polynomial; L], + ct1: [Polynomial; L], sk: [Polynomial; L], e_sm: [Polynomial; L], r1: [Polynomial<(2 * N) - 1>; L], r2: [Polynomial; L], d: [Polynomial; L], + d_native_trunc: [Polynomial; L], ) -> pub Field { - let share_decryption: ShareDecryption = ShareDecryption::new( + let share_decryption: ShareDecryption = ShareDecryption::new( THRESHOLD_SHARE_DECRYPTION_CONFIGS, expected_sk_commitment, expected_e_sm_commitment, + ct_commitment, ct0, ct1, sk, @@ -36,6 +39,7 @@ fn main( r1, r2, d, + d_native_trunc, ); share_decryption.execute() } diff --git a/circuits/lib/src/configs/insecure/threshold.nr b/circuits/lib/src/configs/insecure/threshold.nr index d6b72e22c3..c6741c0e36 100644 --- a/circuits/lib/src/configs/insecure/threshold.nr +++ b/circuits/lib/src/configs/insecure/threshold.nr @@ -1166,6 +1166,7 @@ pub global THRESHOLD_SHARE_DECRYPTION_BIT_E_SM: u32 = 35; pub global THRESHOLD_SHARE_DECRYPTION_BIT_R1: u32 = 43; pub global THRESHOLD_SHARE_DECRYPTION_BIT_R2: u32 = 35; pub global THRESHOLD_SHARE_DECRYPTION_BIT_D: u32 = 35; +pub global THRESHOLD_SHARE_DECRYPTION_BIT_D_NATIVE: u32 = 36; pub global THRESHOLD_SHARE_DECRYPTION_R1_BOUNDS: [Field; L] = [8796083584898, 8796061564802]; pub global THRESHOLD_SHARE_DECRYPTION_R2_BOUNDS: [Field; L] = [34359701504, 34359615488]; @@ -1182,7 +1183,7 @@ decrypted_shares_aggregation (CIRCUIT 7) ------------------------------------- ************************************/ pub global DECRYPTED_SHARES_AGGREGATION_BIT_NOISE: u32 = 65; -pub global DECRYPTED_SHARES_AGGREGATION_BIT_D: u32 = 35; +pub global DECRYPTED_SHARES_AGGREGATION_BIT_D_NATIVE: u32 = 36; pub global DECRYPTED_SHARES_AGGREGATION_CONFIGS: DecryptedSharesAggregationConfigs = DecryptedSharesAggregationConfigs::new(QIS, PLAINTEXT_MODULUS, Q_INVERSE_MOD_T); diff --git a/circuits/lib/src/configs/secure/threshold.nr b/circuits/lib/src/configs/secure/threshold.nr index 86c97edf3b..324cd69694 100644 --- a/circuits/lib/src/configs/secure/threshold.nr +++ b/circuits/lib/src/configs/secure/threshold.nr @@ -24729,6 +24729,7 @@ pub global THRESHOLD_SHARE_DECRYPTION_BIT_E_SM: u32 = 58; pub global THRESHOLD_SHARE_DECRYPTION_BIT_R1: u32 = 70; pub global THRESHOLD_SHARE_DECRYPTION_BIT_R2: u32 = 58; pub global THRESHOLD_SHARE_DECRYPTION_BIT_D: u32 = 58; +pub global THRESHOLD_SHARE_DECRYPTION_BIT_D_NATIVE: u32 = 59; pub global THRESHOLD_SHARE_DECRYPTION_R1_BOUNDS: [Field; L] = [590295810402460628994, 590295810390112598018, 590295810378301437954]; @@ -24748,7 +24749,7 @@ decrypted_shares_aggregation (CIRCUIT 7) ************************************/ pub global DECRYPTED_SHARES_AGGREGATION_BIT_NOISE: u32 = 157; -pub global DECRYPTED_SHARES_AGGREGATION_BIT_D: u32 = 58; +pub global DECRYPTED_SHARES_AGGREGATION_BIT_D_NATIVE: u32 = 59; pub global DECRYPTED_SHARES_AGGREGATION_CONFIGS: DecryptedSharesAggregationConfigs = DecryptedSharesAggregationConfigs::new(QIS, PLAINTEXT_MODULUS, Q_INVERSE_MOD_T); diff --git a/circuits/lib/src/core/dkg/share_encryption.nr b/circuits/lib/src/core/dkg/share_encryption.nr index 6410f17c08..ecec07103c 100644 --- a/circuits/lib/src/core/dkg/share_encryption.nr +++ b/circuits/lib/src/core/dkg/share_encryption.nr @@ -4,10 +4,11 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::math::commitments::compute_dkg_pk_commitment; use crate::math::commitments::{ - compute_share_encryption_challenge, compute_share_encryption_commitment_from_message, + compute_ciphertext_commitment, compute_share_encryption_challenge, + compute_share_encryption_commitment_from_message, }; +use crate::math::commitments::compute_dkg_pk_commitment; use crate::math::helpers::flatten; use crate::math::modulo::U128::ModU128; use crate::math::polynomial::Polynomial; @@ -87,7 +88,7 @@ impl Configs { /// /// **Consumes:** `expected_pk_commitment` (C0); `expected_message_commitment` (C2 sk or C2 e_sm). /// -/// **Produces:** Ciphertexts `ct0`, `ct1` for C4 (no new standalone commitment artifact). +/// **Produces:** Public `ct_commitment` (hash of ct0/ct1 limbs) for cross-phase binding; witness ct0/ct1 feed C4 off-transcript. pub struct ShareEncryption { /// Circuit parameters configs: Configs, @@ -284,8 +285,8 @@ impl Field { // Step 1: Verify public key commitment matches expected self.verify_pk_commitment(); @@ -305,7 +306,9 @@ impl(self.ct0is, self.ct1is) } /// Performs range checks on all secret witness polynomial coefficients. diff --git a/circuits/lib/src/core/threshold/decrypted_shares_aggregation.nr b/circuits/lib/src/core/threshold/decrypted_shares_aggregation.nr index ec0e468bba..f26cdfebbd 100644 --- a/circuits/lib/src/core/threshold/decrypted_shares_aggregation.nr +++ b/circuits/lib/src/core/threshold/decrypted_shares_aggregation.nr @@ -35,7 +35,7 @@ impl Configs { /// **Consumes:** C6 `d` commitments for each reconstructing party (same order as shares). /// /// **Verifies:** Share binding to C6; Lagrange combine per limb; CRT glue; decode mod `t`. -pub struct DecryptedSharesAggregation { +pub struct DecryptedSharesAggregation { /// Circuit parameters including crypto constants configs: Configs, @@ -59,7 +59,7 @@ pub struct DecryptedSharesAggregation; L], } -impl DecryptedSharesAggregation { +impl DecryptedSharesAggregation { pub fn new( configs: Configs, expected_d_commitments: [Field; T + 1], @@ -83,7 +83,7 @@ impl( + let computed = compute_threshold_decryption_share_commitment::( self.decryption_shares[party_idx], ); assert(computed == self.expected_d_commitments[party_idx], "d commitment mismatch"); diff --git a/circuits/lib/src/core/threshold/share_decryption.nr b/circuits/lib/src/core/threshold/share_decryption.nr index 222dd03cf6..83b5b3edac 100644 --- a/circuits/lib/src/core/threshold/share_decryption.nr +++ b/circuits/lib/src/core/threshold/share_decryption.nr @@ -5,8 +5,8 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use crate::math::commitments::{ - compute_aggregated_shares_commitment, compute_threshold_decryption_share_commitment, - compute_threshold_share_decryption_challenge, + compute_aggregated_shares_commitment, compute_ciphertext_commitment, + compute_threshold_decryption_share_commitment, compute_threshold_share_decryption_challenge, }; use crate::math::helpers::flatten; use crate::math::polynomial::Polynomial; @@ -32,10 +32,11 @@ impl Configs { /// **Role:** Prove one party's partial decryption share `d` is consistent with the ciphertext and /// with aggregate `sk` / `e_sm` commitments from C4. /// -/// **Consumes:** `expected_sk_commitment`, `expected_e_sm_commitment` (C4); ciphertext limbs `ct0`, `ct1`. +/// **Consumes:** `expected_sk_commitment`, `expected_e_sm_commitment` (C4), `ct_commitment` (cross-phase / +/// user data encryption); ciphertext limbs `ct0`, `ct1` as witness. /// /// **Produces:** Truncated-`d` commitment for C7 (`decrypted_shares_aggregation`). -pub struct ShareDecryption { +pub struct ShareDecryption { /// Circuit parameters including bounds and cryptographic constants configs: Configs, @@ -47,7 +48,10 @@ pub struct ShareDecryption; L], /// ct1 components for each CRT basis (degree N-1 polynomials with N coefficients) @@ -66,13 +70,20 @@ pub struct ShareDecryption; L], + + /// Low-degree tail of `d` in **native** \([0, q)\) form per coefficient (matches C7 + /// `decryption_shares` / `from_fhe` truncation). Coefficients are in the same JSON order + /// as C7 (ascending degree within each limb). `d_commitment` hashes this, not the + /// reversed+centered `d` prefix, so DecryptionAggregator can link C6 fold to C7. + d_native_trunc: [Polynomial; L], } -impl ShareDecryption { +impl ShareDecryption { pub fn new( configs: Configs, expected_sk_commitment: Field, expected_e_sm_commitment: Field, + ct_commitment: Field, ct0: [Polynomial; L], ct1: [Polynomial; L], sk: [Polynomial; L], @@ -80,12 +91,14 @@ impl; L], r2: [Polynomial; L], d: [Polynomial; L], + d_native_trunc: [Polynomial; L], ) -> Self { assert(MAX_MSG_NON_ZERO_COEFFS <= N); ShareDecryption { configs, expected_sk_commitment, expected_e_sm_commitment, + ct_commitment, ct0, ct1, sk, @@ -93,6 +106,35 @@ impl(); + (q - 1 - u).assert_max_bit_size::<64>(); + let centered_from_u = if (u as u128) > (half as u128) { + u - q + } else { + u + }; + assert(centered_from_u == w); + } + + /// Bind each `d_native_trunc` coeff to the tail of `d` under `center(mod q)` / reverse layout. + fn verify_d_native_trunc_binding(self) { + for limb in 0..L { + let q = self.configs.qis[limb]; + for j in 0..MAX_MSG_NON_ZERO_COEFFS { + let w = self.d[limb].coefficients[N - 1 - j]; + // `d_native_trunc` uses the same limb coefficient order as C7 witness JSON (ascending degree). + let u = self.d_native_trunc[limb].coefficients[j]; + Self::assert_native_coeff_matches_centered_d(u, w, q); + } } } @@ -114,20 +156,9 @@ impl [Polynomial; L] { - let mut out: [Polynomial; L] = - [Polynomial::new([0; MAX_MSG_NON_ZERO_COEFFS]); L]; - for i in 0..L { - let mut coeffs = [0; MAX_MSG_NON_ZERO_COEFFS]; - for j in 0..MAX_MSG_NON_ZERO_COEFFS { - coeffs[j] = self.d[i].coefficients[j]; - } - out[i] = Polynomial::new(coeffs); - } - out + fn verify_ct_commitment(self) { + let computed = compute_ciphertext_commitment::(self.ct0, self.ct1); + assert(computed == self.ct_commitment, "Ciphertext commitment mismatch"); } /// Flattens all witness data into a single array for Fiat-Shamir challenge generation. @@ -159,7 +190,7 @@ impl(inputs, self.ct0); inputs = flatten::<_, _, BIT_CT>(inputs, self.ct1); @@ -181,20 +212,23 @@ impl( - d_truncated, + self.verify_d_native_trunc_binding(); + let d_commitment = compute_threshold_decryption_share_commitment::( + self.d_native_trunc, ); - // Step 4: Generate Fiat-Shamir challenge from the transcript + // Step 5: Generate Fiat-Shamir challenge from the transcript let gamma = self.generate_challenge(); - // Step 5: Verify decryption share computation for each CRT basis + // Step 6: Verify decryption share computation for each CRT basis for i in 0..L { self.verify_decryption_share_computation(i, gamma); } diff --git a/circuits/lib/src/math/commitments.nr b/circuits/lib/src/math/commitments.nr index 909009a64d..cb6f3fbcbe 100644 --- a/circuits/lib/src/math/commitments.nr +++ b/circuits/lib/src/math/commitments.nr @@ -200,24 +200,6 @@ pub fn compute_share_encryption_commitment_from_message( - y: [[[Field; N_PARTIES + 1]; L]; N], - party_idx: u32, - mod_idx: u32, -) -> Field { - let mut payload = Vec::new(); - - for coeff_idx in 0..N { - payload.push(y[coeff_idx][mod_idx][party_idx + 1]); - } - - // Include party_idx and mod_idx in the hash - payload.push(party_idx as Field); - payload.push(mod_idx as Field); - - compute_commitment(payload, DS_SHARE_ENCRYPTION) -} - pub fn compute_aggregated_shares_commitment( agg_shares: [Polynomial; L], ) -> Field { @@ -227,10 +209,13 @@ pub fn compute_aggregated_shares_commitment` in decrypted_shares_aggregation). -pub fn compute_threshold_decryption_share_commitment( +/// +/// `NATIVE_BIT_WIDTH` must cover native coefficients in \([0, q_l)\) per limb (not centered `d` bounds). +pub fn compute_threshold_decryption_share_commitment( d_share_limbs: [Polynomial; L], ) -> Field { - let mut payload = multiple_polynomial_payload::(Vec::new(), d_share_limbs); + let mut payload = + multiple_polynomial_payload::(Vec::new(), d_share_limbs); compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE) } diff --git a/crates/aggregator/src/ext.rs b/crates/aggregator/src/ext.rs index d7712e8215..1c794e4db6 100644 --- a/crates/aggregator/src/ext.rs +++ b/crates/aggregator/src/ext.rs @@ -213,6 +213,7 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { sortition: self.sortition.clone(), e3_id: e3_id.clone(), params_preset: meta.params_preset, + proof_aggregation_enabled: meta.proof_aggregation_enabled, }, sync_state, ) @@ -252,6 +253,7 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { sortition: self.sortition.clone(), e3_id: ctx.e3_id.clone(), params_preset: meta.params_preset, + proof_aggregation_enabled: meta.proof_aggregation_enabled, }, sync_state, ) diff --git a/crates/aggregator/src/lib.rs b/crates/aggregator/src/lib.rs index 348ac8976d..1738a79a6d 100644 --- a/crates/aggregator/src/lib.rs +++ b/crates/aggregator/src/lib.rs @@ -8,7 +8,6 @@ mod committee_finalizer; mod decryptionshare_created_buffer; pub mod ext; mod keyshare_created_filter_buffer; -mod proof_fold; mod publickey_aggregator; mod repo; mod threshold_plaintext_aggregator; diff --git a/crates/aggregator/src/proof_fold.rs b/crates/aggregator/src/proof_fold.rs deleted file mode 100644 index 00614f9f68..0000000000 --- a/crates/aggregator/src/proof_fold.rs +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use anyhow::Result; -use e3_events::{ - prelude::*, BusHandle, ComputeRequest, CorrelationId, E3id, EventContext, Proof, Sequenced, - ZkRequest, -}; -use e3_fhe_params::BfvPreset; -use tracing::{error, info}; - -/// Manages the state of a sequential `FoldProofs` operation. -/// -/// Takes an ordered list of proofs and folds them pairwise via `ZkRequest::FoldProofs` -/// until a single aggregated proof remains. The caller owns the struct and checks -/// `result` (or calls `is_complete`) to know when folding is done. -/// -/// Serialization support enables persistence during VerifyingC1/GeneratingC5Proof for restart recovery. -#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)] -pub struct ProofFoldState { - correlation: Option, - accumulated: Option, - remaining: Vec, - /// Total fold steps (for progress logging). Set when fold starts. - total_steps: Option, - /// Set when all fold steps have completed. - pub result: Option, - /// `start` was called with zero proofs — folding is complete with no aggregate. - pub fold_input_was_empty: bool, - /// BFV preset for circuit artifact resolution. - params_preset: BfvPreset, -} - -impl ProofFoldState { - pub fn new(params_preset: BfvPreset) -> Self { - ProofFoldState { - correlation: None, - accumulated: None, - remaining: Vec::new(), - total_steps: None, - result: None, - fold_input_was_empty: false, - params_preset, - } - } - - /// Returns `true` if a fold step was dispatched but the in-flight proof was consumed - /// and the response will never arrive (e.g. after a restart). The caller should reset - /// and re-initiate the fold from the original proofs. - pub fn needs_restart(&self) -> bool { - self.correlation.is_some() && self.accumulated.is_none() && self.result.is_none() - } - - /// Returns `true` if no fold has been initiated yet (idle / not started). - pub fn is_idle(&self) -> bool { - self.correlation.is_none() - && self.accumulated.is_none() - && self.remaining.is_empty() - && self.total_steps.is_none() - && self.result.is_none() - } - - /// `true` if a fold step is in flight and expects this `ComputeResponse` correlation. - pub fn awaits_correlation(&self, correlation_id: &CorrelationId) -> bool { - self.correlation.as_ref() == Some(correlation_id) - } - - /// Begin folding `proofs` sequentially. - /// - /// - 0 proofs → `result` stays `None`, `fold_input_was_empty` is set (caller can treat fold as done) - /// - 1 proof → `result` is set immediately, no ZK request dispatched - /// - N proofs → first fold step dispatched; subsequent steps follow via `handle_response` - pub fn start( - &mut self, - mut proofs: Vec, - label: &str, - bus: &BusHandle, - e3_id: &E3id, - ec: &EventContext, - ) -> Result<()> { - self.correlation = None; - self.accumulated = None; - self.remaining.clear(); - self.total_steps = None; - self.result = None; - self.fold_input_was_empty = false; - - match proofs.len() { - 0 => { - info!("{label}: no proofs to fold"); - self.fold_input_was_empty = true; - Ok(()) - } - 1 => { - info!("{label}: single proof — no fold needed"); - self.result = Some(proofs.remove(0)); - Ok(()) - } - _ => { - let first = proofs.remove(0); - self.accumulated = Some(first); - self.remaining = proofs; - let total = self.remaining.len(); - self.total_steps = Some(total); - info!( - "{label}: starting fold ({} steps total, {} proofs remaining)", - total, - self.remaining.len() - ); - self.advance(label, bus, e3_id, ec) - } - } - } - - /// Handle a `FoldProofs` response. Returns `true` if `correlation_id` matched this fold. - /// - /// On match, either dispatches the next step or finalises `result`. - pub fn handle_response( - &mut self, - correlation_id: &CorrelationId, - proof: Proof, - label: &str, - bus: &BusHandle, - e3_id: &E3id, - ec: &EventContext, - ) -> Result { - let Some(expected) = self.correlation else { - return Ok(false); - }; - if expected != *correlation_id { - return Ok(false); - } - - self.correlation = None; - self.accumulated = Some(proof); - - let step_done = self - .total_steps - .map(|t| t - self.remaining.len()) - .unwrap_or(0); - info!( - "{label}: fold step {}/{} done ({} remaining)", - step_done, - self.total_steps.unwrap_or(0), - self.remaining.len() - ); - - if self.remaining.is_empty() { - self.result = self.accumulated.take(); - } else { - self.advance(label, bus, e3_id, ec)?; - } - - Ok(true) - } - - fn advance( - &mut self, - label: &str, - bus: &BusHandle, - e3_id: &E3id, - ec: &EventContext, - ) -> Result<()> { - if self.correlation.is_some() { - return Ok(()); - } - - let Some(acc) = self.accumulated.take() else { - return Ok(()); - }; - - let Some(next) = self.remaining.first().cloned() else { - self.result = Some(acc); - return Ok(()); - }; - self.remaining.remove(0); - let target_evm = self.remaining.is_empty(); - - let corr = CorrelationId::new(); - self.correlation = Some(corr); - - let step = self - .total_steps - .map(|t| t - self.remaining.len()) - .unwrap_or(0); - info!( - "{label}: dispatching fold step {}/{} ({} proofs remaining, target_evm={})", - step, - self.total_steps.unwrap_or(0), - self.remaining.len(), - target_evm - ); - - if let Err(err) = bus.publish( - ComputeRequest::zk( - ZkRequest::FoldProofs { - proof1: acc, - proof2: next, - target_evm, - params_preset: self.params_preset, - }, - corr, - e3_id.clone(), - ), - ec.clone(), - ) { - error!("{label}: failed to publish fold request: {err}"); - self.correlation = None; - } - - Ok(()) - } -} diff --git a/crates/aggregator/src/publickey_aggregator.rs b/crates/aggregator/src/publickey_aggregator.rs index 371670b69d..6d46808047 100644 --- a/crates/aggregator/src/publickey_aggregator.rs +++ b/crates/aggregator/src/publickey_aggregator.rs @@ -4,17 +4,17 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::proof_fold::ProofFoldState; use actix::prelude::*; use anyhow::Result; use e3_data::Persistable; use e3_events::{ - prelude::*, BusHandle, ComputeResponse, ComputeResponseKind, DKGRecursiveAggregationComplete, - Die, E3Failed, E3Stage, E3id, EnclaveEvent, EnclaveEventData, EventContext, FailureReason, - KeyshareCreated, OrderedSet, PartyProofsToVerify, PkAggregationProofPending, - PkAggregationProofRequest, PkAggregationProofSigned, Proof, ProofType, ProofVerificationPassed, - PublicKeyAggregated, Seed, Sequenced, ShareVerificationComplete, ShareVerificationDispatched, - SignedProofFailed, SignedProofPayload, TypedEvent, VerificationKind, ZkResponse, + prelude::*, BusHandle, CircuitName, ComputeRequest, ComputeResponse, ComputeResponseKind, + CorrelationId, DKGRecursiveAggregationComplete, Die, DkgAggregationRequest, E3Failed, E3Stage, + E3id, EnclaveEvent, EnclaveEventData, EventContext, FailureReason, KeyshareCreated, OrderedSet, + PartyProofsToVerify, PkAggregationProofPending, PkAggregationProofRequest, + PkAggregationProofSigned, Proof, ProofType, PublicKeyAggregated, Seed, Sequenced, + ShareVerificationComplete, ShareVerificationDispatched, SignedProofFailed, SignedProofPayload, + TypedEvent, VerificationKind, ZkRequest, ZkResponse, }; use e3_events::{trap, EType}; use e3_fhe::{Fhe, GetAggregatePublicKey}; @@ -25,29 +25,52 @@ use std::collections::{BTreeSet, HashMap}; use std::sync::Arc; use tracing::{error, info, warn}; +/// Public-signal key for the aggregated PK commitment in `CircuitName::PkAggregation` (C5). +/// Must stay in lock-step with the Noir circuit's output ABI declaration. +const C5_PK_COMMITMENT_FIELD: &str = "commitment"; + +/// Extract the hash-based aggregated PK commitment from the signed C5 proof. +/// This is the last public signal of `CircuitName::PkAggregation`. +fn extract_pk_commitment(c5_proof: &Proof) -> Result<[u8; 32]> { + let layout = CircuitName::PkAggregation.output_layout(); + let bytes = layout + .extract_field(&c5_proof.public_signals, C5_PK_COMMITMENT_FIELD) + .ok_or_else(|| anyhow::anyhow!("C5 proof is missing `commitment` public signal"))?; + let mut out = [0u8; 32]; + if bytes.len() != 32 { + return Err(anyhow::anyhow!( + "C5 `commitment` public signal must be 32 bytes" + )); + } + out.copy_from_slice(bytes); + Ok(out) +} + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum PublicKeyAggregatorState { Collecting { threshold_n: usize, threshold_m: usize, keyshares: OrderedSet, - /// C1 proofs collected from KeyshareCreated events, indexed by insertion order. + /// C1 proofs collected from KeyshareCreated events, indexed by insertion order + /// (matches `submission_order`). c1_proofs: Vec>, seed: Seed, nodes: OrderedSet, - /// Insertion-ordered (node, keyshare) pairs. - /// Index matches `c1_proofs`, giving the party ID for verification. + /// Insertion-ordered (real sortition `party_id`, node, keyshare) triples. + /// Index matches `c1_proofs`. The real `party_id` comes from `KeyshareCreated` + /// and must be used for all downstream circuit slot indexing — arrival order + /// is non-deterministic and does not match sortition's committee position. #[serde(default)] - submission_order: Vec<(String, ArcBytes)>, + submission_order: Vec<(u64, String, ArcBytes)>, }, VerifyingC1 { - /// Insertion-ordered (node, keyshare) pairs from Collecting. - /// Index matches `c1_proofs`, giving the party ID used in verification. - submission_order: Vec<(String, ArcBytes)>, + /// Insertion-ordered (party_id, node, keyshare) triples from Collecting. + submission_order: Vec<(u64, String, ArcBytes)>, threshold_m: usize, /// C1 proofs in the same insertion order as `submission_order`. c1_proofs: Vec>, - /// Party indices that submitted no C1 proof — treated as dishonest. + /// Real party_ids that submitted no C1 proof — treated as dishonest. no_proof_parties: Vec, }, GeneratingC5Proof { @@ -58,7 +81,10 @@ pub enum PublicKeyAggregatorState { dkg_node_proofs: HashMap>, honest_party_ids: BTreeSet, dishonest_parties: BTreeSet, - cross_node_fold: ProofFoldState, + /// In-flight [`ZkRequest::DkgAggregation`], if any. + dkg_aggregation_correlation: Option, + /// Result from [`ZkResponse::DkgAggregation`] (replaces pairwise `FoldProofs`). + dkg_aggregated_proof: Option, c5_proof_pending: Option, last_ec: Option>, }, @@ -122,6 +148,7 @@ impl PublicKeyAggregator { &mut self, keyshare: ArcBytes, node: String, + party_id: u64, c1_proof: Option, ec: &EventContext, ) -> Result<()> { @@ -142,7 +169,13 @@ impl PublicKeyAggregator { keyshares.insert(keyshare.clone()); c1_proofs.push(c1_proof); nodes.insert(node.clone()); - submission_order.push((node, keyshare)); + info!( + "add_keyshare: node={} party_id={} (arrival slot={})", + node, + party_id, + submission_order.len() + ); + submission_order.push((party_id, node, keyshare)); let n = *threshold_n; let m = *threshold_m; info!( @@ -166,26 +199,27 @@ impl PublicKeyAggregator { fn dispatch_c1_verification( &mut self, + submission_order: &[(u64, String, ArcBytes)], c1_proofs: &[Option], ec: EventContext, ) -> Result<()> { let mut party_proofs = Vec::new(); let mut no_proof_parties = Vec::new(); - for (idx, proof_opt) in c1_proofs.iter().enumerate() { + for ((party_id, _, _), proof_opt) in submission_order.iter().zip(c1_proofs.iter()) { match proof_opt { Some(proof) => { party_proofs.push(PartyProofsToVerify { - sender_party_id: idx as u64, + sender_party_id: *party_id, signed_proofs: vec![proof.clone()], }); } None => { warn!( "Party {} submitted keyshare without C1 proof — treating as dishonest", - idx + party_id ); - no_proof_parties.push(idx as u64); + no_proof_parties.push(*party_id); } } } @@ -260,29 +294,31 @@ impl PublicKeyAggregator { }; let mut dishonest_parties = msg.dishonest_parties.clone(); - let total_parties = submission_order.len(); - // Filter out parties that failed C1 ZK verification. - let mut honest_entries: Vec<(usize, (String, ArcBytes))> = submission_order - .into_iter() - .enumerate() - .filter(|(idx, _)| !dishonest_parties.contains(&(*idx as u64))) - .collect(); + // Filter out parties that failed C1 ZK verification. Keyed by the real + // sortition party_id carried in `submission_order`, not arrival index. + let mut honest_entries: Vec<(u64, String, ArcBytes, Option)> = + submission_order + .into_iter() + .zip(c1_proofs.into_iter()) + .filter(|((pid, _, _), _)| !dishonest_parties.contains(pid)) + .map(|((pid, node, ks), c1)| (pid, node, ks, c1)) + .collect(); // Cross-check: verify each party's keyshare matches their C1 pk_commitment. // Parties that fail are marked dishonest and reported via SignedProofFailed. let mut commitment_dishonest = Vec::new(); - for (party_idx, (_node, ks)) in &honest_entries { - let signed_proof = match c1_proofs.get(*party_idx).and_then(|opt| opt.as_ref()) { + for (party_id, _node, ks, c1) in &honest_entries { + let signed_proof = match c1.as_ref() { Some(proof) => proof, None => { // No C1 proof for this party — should already be in dishonest_parties. // If not, treat as dishonest now (defensive). warn!( "Party {} has no C1 proof but was not marked dishonest", - party_idx + party_id ); - dishonest_parties.insert(*party_idx as u64); + dishonest_parties.insert(*party_id); continue; } }; @@ -299,19 +335,19 @@ impl PublicKeyAggregator { Err(e) => { warn!( "Failed to compute pk_commitment for party {}: {}", - party_idx, e + party_id, e ); false } }; if !ok { - commitment_dishonest.push((*party_idx as u64, signed_proof.clone())); + commitment_dishonest.push((*party_id, signed_proof.clone())); } } // Emit SignedProofFailed for each commitment-mismatched party - for (party_idx, signed_proof) in &commitment_dishonest { - dishonest_parties.insert(*party_idx); + for (party_id, signed_proof) in &commitment_dishonest { + dishonest_parties.insert(*party_id); match signed_proof.recover_address() { Ok(faulting_node) => { if let Err(e) = self.bus.publish( @@ -328,7 +364,7 @@ impl PublicKeyAggregator { } Err(e) => warn!( "Could not recover address from C1 proof for party {}: {e}", - party_idx + party_id ), } } @@ -339,12 +375,18 @@ impl PublicKeyAggregator { commitment_dishonest.len() ); // Re-filter honest_entries after commitment check - honest_entries.retain(|(idx, _)| !dishonest_parties.contains(&(*idx as u64))); + honest_entries.retain(|(pid, _, _, _)| !dishonest_parties.contains(pid)); } + // Sort by real party_id ascending so honest_keyshares / honest_nodes / + // honest_party_ids all share the same ordering used by NodeFold rows + // (publickey_aggregator sorts dkg_node_proofs by pid before dispatch) + // and by the circuit's slot indexing in `dkg_aggregator.nr`. + honest_entries.sort_by_key(|(pid, _, _, _)| *pid); + let (honest_keyshares, honest_nodes): (Vec, Vec) = honest_entries .iter() - .map(|(_, (node, ks))| (ks.clone(), node.clone())) + .map(|(_, node, ks, _)| (ks.clone(), node.clone())) .unzip(); if !dishonest_parties.is_empty() { @@ -354,9 +396,8 @@ impl PublicKeyAggregator { ); } - let honest_party_ids: BTreeSet = (0..total_parties as u64) - .filter(|id| !dishonest_parties.contains(id)) - .collect(); + let honest_party_ids: BTreeSet = + honest_entries.iter().map(|(pid, _, _, _)| *pid).collect(); // Need at least threshold + 1 honest parties for aggregation if honest_keyshares.len() <= threshold_m { @@ -388,7 +429,12 @@ impl PublicKeyAggregator { let committee_h = honest_keyshares.len(); let honest_nodes_set = OrderedSet::from(honest_nodes.clone()); - let keyshare_bytes: Vec<_> = honest_keyshares_set.iter().cloned().collect(); + // Feed keyshares to C5 in ascending party_id order so that + // `c5_public[i]` (pk_commitment of the i-th input keyshare) matches + // party_ids[i] and the row-i node_fold pk bound by dkg_aggregator.nr. + // `honest_keyshares` preserves the submission-index (== party_id) order + // from `honest_entries`; do NOT sort by byte content. + let keyshare_bytes: Vec = honest_keyshares.clone(); let pubkey = ArcBytes::from_bytes(&pubkey); info!("Publishing PkAggregationProofPending for C5 proof generation..."); @@ -417,7 +463,8 @@ impl PublicKeyAggregator { dkg_node_proofs: HashMap::new(), honest_party_ids: honest_party_ids.clone(), dishonest_parties: dishonest_parties.clone(), - cross_node_fold: ProofFoldState::new(self.params_preset), + dkg_aggregation_correlation: None, + dkg_aggregated_proof: None, c5_proof_pending: None, last_ec: Some(ec.clone()), }) @@ -429,7 +476,7 @@ impl PublicKeyAggregator { self.handle_dkg_recursive_aggregation_complete(event)?; } - self.try_start_cross_node_fold(&ec)?; + self.try_dispatch_dkg_aggregation(&ec)?; Ok(()) } @@ -464,7 +511,8 @@ impl PublicKeyAggregator { dkg_node_proofs, honest_party_ids, dishonest_parties, - cross_node_fold, + dkg_aggregation_correlation, + dkg_aggregated_proof, .. } = state else { @@ -477,7 +525,8 @@ impl PublicKeyAggregator { dkg_node_proofs, honest_party_ids, dishonest_parties, - cross_node_fold, + dkg_aggregation_correlation, + dkg_aggregated_proof, c5_proof_pending: Some(c5_proof), last_ec: Some(ec.clone()), }) @@ -531,7 +580,8 @@ impl PublicKeyAggregator { mut dkg_node_proofs, honest_party_ids, dishonest_parties, - cross_node_fold, + dkg_aggregation_correlation, + dkg_aggregated_proof, c5_proof_pending, last_ec: _, } = state @@ -546,55 +596,110 @@ impl PublicKeyAggregator { dkg_node_proofs, honest_party_ids, dishonest_parties, - cross_node_fold, + dkg_aggregation_correlation, + dkg_aggregated_proof, c5_proof_pending, last_ec: Some(ec.clone()), }) })?; - self.try_start_cross_node_fold(&ec) + self.try_dispatch_dkg_aggregation(&ec) } - /// Start cross-node fold once we have DKG proofs from all verified honest parties. - fn try_start_cross_node_fold(&mut self, ec: &EventContext) -> Result<()> { + /// Dispatch [`ZkRequest::DkgAggregation`] once C5 and all honest NodeFold proofs are ready. + fn try_dispatch_dkg_aggregation(&mut self, ec: &EventContext) -> Result<()> { let state = self.state.get(); let Some(PublicKeyAggregatorState::GeneratingC5Proof { dkg_node_proofs, honest_party_ids, - cross_node_fold, + c5_proof_pending, + dkg_aggregation_correlation, + dkg_aggregated_proof, .. }) = state.as_ref() else { return Ok(()); }; + + let Some(c5_proof) = c5_proof_pending.as_ref() else { + return Ok(()); + }; + + if dkg_aggregation_correlation.is_some() || dkg_aggregated_proof.is_some() { + return Ok(()); + } + let all_honest_proofs_present = honest_party_ids .iter() .all(|id| dkg_node_proofs.contains_key(id)); - if !all_honest_proofs_present - || (!cross_node_fold.is_idle() && !cross_node_fold.needs_restart()) - { + if !all_honest_proofs_present { return Ok(()); } - // Collect non-None proofs from honest parties for cross-node folding. - // Folding is skipped only when all honest-party proofs are None (every node - // reported aggregation disabled). A mixed Some/None scenario should not occur - // in practice because proof_aggregation_enabled is an E3-level flag shared by all nodes. + // `proof_aggregation_enabled` is an E3-level flag shared by all nodes, so honest-party + // proofs should be uniformly Some (aggregation on) or uniformly None (aggregation off). + // A mixed bag would silently truncate the dispatched request below; reject it explicitly. + let some_count = honest_party_ids + .iter() + .filter(|id| { + dkg_node_proofs + .get(id) + .map(Option::is_some) + .unwrap_or(false) + }) + .count(); + if some_count != 0 && some_count != honest_party_ids.len() { + anyhow::bail!( + "PublicKeyAggregator: mixed Some/None DKG node proofs across honest parties \ + ({some_count} of {} present); refusing to dispatch a truncated DkgAggregation", + honest_party_ids.len() + ); + } + let mut pairs: Vec<_> = dkg_node_proofs .iter() .filter(|(pid, _)| honest_party_ids.contains(pid)) .filter_map(|(pid, p)| p.as_ref().map(|proof| (*pid, proof.clone()))) .collect(); pairs.sort_by_key(|(pid, _)| *pid); - let proofs: Vec = pairs.into_iter().map(|(_, p)| p).collect(); + let party_ids: Vec = pairs.iter().map(|(pid, _)| *pid).collect(); + let node_fold_proofs: Vec = pairs.into_iter().map(|(_, p)| p).collect(); + info!( + "ORDER-DEBUG dispatch DkgAggregation: honest_party_ids(submission-idx)={:?} \ + dkg_node_proofs_keys(real party_id from DKGRecursiveAggregationComplete)={:?} \ + party_ids_passed_to_circuit={:?}", + honest_party_ids.iter().collect::>(), + { + let mut k: Vec = dkg_node_proofs.keys().copied().collect(); + k.sort(); + k + }, + party_ids + ); - // If no proofs to fold (aggregation was disabled), try publishing immediately - if proofs.is_empty() { - info!("PublicKeyAggregator: proof aggregation disabled — skipping cross-node fold"); - self.try_publish_complete()?; + if node_fold_proofs.is_empty() { + // Proof aggregation disabled. Do NOT call `try_publish_complete` here — it + // is the most common entry into this method, so re-entering it would create + // unbounded mutual recursion (stack overflow in deployed nodes). + info!("PublicKeyAggregator: proof aggregation disabled — skipping DkgAggregation"); return Ok(()); } + let corr = CorrelationId::new(); + self.bus.publish( + ComputeRequest::zk( + ZkRequest::DkgAggregation(DkgAggregationRequest { + node_fold_proofs, + c5_proof: c5_proof.clone(), + party_ids, + params_preset: self.params_preset, + }), + corr, + self.e3_id.clone(), + ), + ec.clone(), + )?; + self.state.try_mutate(ec, |state| { let PublicKeyAggregatorState::GeneratingC5Proof { public_key, @@ -603,24 +708,14 @@ impl PublicKeyAggregator { dkg_node_proofs, honest_party_ids, dishonest_parties, - mut cross_node_fold, + dkg_aggregation_correlation: _, + dkg_aggregated_proof, c5_proof_pending, last_ec, } = state else { return Ok(state); }; - if cross_node_fold.needs_restart() { - warn!("cross-node fold stuck mid-step on restart — resetting and re-folding from persisted proofs"); - cross_node_fold = ProofFoldState::new(self.params_preset); - } - cross_node_fold.start( - proofs, - "PublicKeyAggregator cross-node DKG fold", - &self.bus, - &self.e3_id, - ec, - )?; Ok(PublicKeyAggregatorState::GeneratingC5Proof { public_key, keyshare_bytes, @@ -628,21 +723,33 @@ impl PublicKeyAggregator { dkg_node_proofs, honest_party_ids, dishonest_parties, - cross_node_fold, + dkg_aggregation_correlation: Some(corr), + dkg_aggregated_proof, c5_proof_pending, last_ec, }) })?; - self.try_publish_complete() + Ok(()) } - /// Publish `PublicKeyAggregated` when both C5 and cross-node fold are complete. + /// Publish `PublicKeyAggregated` when C5 (non-ZK recursive) and, if applicable, the EVM DkgAggregator proof are ready (or aggregation skipped). fn try_publish_complete(&mut self) -> Result<()> { + if let Some(ec) = self.state.get().and_then(|s| { + if let PublicKeyAggregatorState::GeneratingC5Proof { last_ec, .. } = &s { + last_ec.clone() + } else { + None + } + }) { + self.try_dispatch_dkg_aggregation(&ec)?; + } + let PublicKeyAggregatorState::GeneratingC5Proof { public_key, nodes, c5_proof_pending, - cross_node_fold, + dkg_aggregated_proof, + dkg_aggregation_correlation: _, last_ec, .. } = self @@ -657,10 +764,6 @@ impl PublicKeyAggregator { return Ok(()); }; - // Cross-node fold result is optional — None when proof aggregation is disabled - let dkg_aggregated_proof = cross_node_fold.result.clone(); - - // If aggregation is enabled but fold hasn't completed yet, wait let all_proofs_are_none = self .state .get() @@ -681,8 +784,7 @@ impl PublicKeyAggregator { }) .unwrap_or(false); - if dkg_aggregated_proof.is_none() && !all_proofs_are_none { - // Aggregation is enabled but fold not done yet — wait + if !all_proofs_are_none && dkg_aggregated_proof.is_none() { return Ok(()); } @@ -691,7 +793,7 @@ impl PublicKeyAggregator { .ok_or_else(|| anyhow::anyhow!("No EventContext for publish"))?; info!( - "C5 proof ready — publishing PublicKeyAggregated (dkg_aggregated_proof={})", + "Publishing PublicKeyAggregated (dkg_evm_proof={})", if dkg_aggregated_proof.is_some() { "present" } else { @@ -699,16 +801,17 @@ impl PublicKeyAggregator { } ); + let pk_commitment = extract_pk_commitment(c5_proof)?; + let event = PublicKeyAggregated { pubkey: public_key.clone(), e3_id: self.e3_id.clone(), nodes: nodes.clone(), - pk_aggregation_proof: Some(c5_proof.clone()), - dkg_aggregated_proof, + pk_commitment, + dkg_aggregator_proof: dkg_aggregated_proof.clone(), }; self.bus.publish(event, ec.clone())?; - // Transition to Complete self.state.try_mutate(&ec, |_| { Ok(PublicKeyAggregatorState::Complete { public_key, @@ -722,18 +825,19 @@ impl PublicKeyAggregator { fn handle_compute_response(&mut self, msg: TypedEvent) -> Result<()> { let (msg, _ec) = msg.into_components(); - if let ComputeResponseKind::Zk(ZkResponse::FoldProofs(resp)) = msg.response { + if let ComputeResponseKind::Zk(ZkResponse::DkgAggregation(resp)) = msg.response { if msg.e3_id != self.e3_id { return Ok(()); } let state = self.state.get(); let Some(PublicKeyAggregatorState::GeneratingC5Proof { last_ec, .. }) = state.as_ref() else { - // Late response after transitioning out of GeneratingC5Proof — ignore. return Ok(()); }; - let Some(ec) = last_ec.clone() else { - return Err(anyhow::anyhow!("No EventContext for fold response")); + let Some(_ec) = last_ec.clone() else { + return Err(anyhow::anyhow!( + "No EventContext for DkgAggregation response" + )); }; self.state.try_mutate_without_context(|state| { let PublicKeyAggregatorState::GeneratingC5Proof { @@ -743,21 +847,28 @@ impl PublicKeyAggregator { dkg_node_proofs, honest_party_ids, dishonest_parties, - mut cross_node_fold, + dkg_aggregation_correlation, + dkg_aggregated_proof, c5_proof_pending, last_ec, } = state else { return Ok(state); }; - cross_node_fold.handle_response( - &msg.correlation_id, - resp.proof.clone(), - "PublicKeyAggregator cross-node DKG fold", - &self.bus, - &self.e3_id, - &ec, - )?; + if dkg_aggregation_correlation.as_ref() != Some(&msg.correlation_id) { + return Ok(PublicKeyAggregatorState::GeneratingC5Proof { + public_key, + keyshare_bytes, + nodes, + dkg_node_proofs, + honest_party_ids, + dishonest_parties, + dkg_aggregation_correlation, + dkg_aggregated_proof, + c5_proof_pending, + last_ec, + }); + } Ok(PublicKeyAggregatorState::GeneratingC5Proof { public_key, keyshare_bytes, @@ -765,7 +876,8 @@ impl PublicKeyAggregator { dkg_node_proofs, honest_party_ids, dishonest_parties, - cross_node_fold, + dkg_aggregation_correlation: None, + dkg_aggregated_proof: Some(resp.proof.clone()), c5_proof_pending, last_ec, }) @@ -798,8 +910,8 @@ impl PublicKeyAggregator { // Find the expelled node's index in submission_order and remove from // all parallel collections so they stay aligned. - if let Some(idx) = submission_order.iter().position(|(n, _)| n == &node_str) { - let (_, expelled_keyshare) = submission_order.remove(idx); + if let Some(idx) = submission_order.iter().position(|(_, n, _)| n == &node_str) { + let (_, _, expelled_keyshare) = submission_order.remove(idx); keyshares.remove(&expelled_keyshare); c1_proofs.remove(idx); } @@ -895,10 +1007,17 @@ impl Handler for PublicKeyAggregator { // using the c1_proofs now stored in the VerifyingC1 state (already // cleaned of the expelled node's entry). if was_collecting { - if let Some(PublicKeyAggregatorState::VerifyingC1 { c1_proofs, .. }) = - self.state.get() + if let Some(PublicKeyAggregatorState::VerifyingC1 { + submission_order, + c1_proofs, + .. + }) = self.state.get() { - self.dispatch_c1_verification(&c1_proofs, ec.clone())?; + self.dispatch_c1_verification( + &submission_order, + &c1_proofs, + ec.clone(), + )?; } } Ok(()) @@ -922,6 +1041,7 @@ impl Handler> for PublicKeyAggregator { let e3_id = event.e3_id.clone(); let pubkey = event.pubkey.clone(); let node = event.node.clone(); + let party_id = event.party_id; let c1_proof = event.signed_pk_generation_proof.clone(); if e3_id != self.e3_id { @@ -929,13 +1049,17 @@ impl Handler> for PublicKeyAggregator { return Ok(()); } - self.add_keyshare(pubkey, node, c1_proof, &ec)?; + self.add_keyshare(pubkey, node, party_id, c1_proof, &ec)?; // If we just transitioned to VerifyingC1, dispatch verification // using c1_proofs stored in the new state. - if let Some(PublicKeyAggregatorState::VerifyingC1 { c1_proofs, .. }) = self.state.get() + if let Some(PublicKeyAggregatorState::VerifyingC1 { + submission_order, + c1_proofs, + .. + }) = self.state.get() { - self.dispatch_c1_verification(&c1_proofs, ec)?; + self.dispatch_c1_verification(&submission_order, &c1_proofs, ec)?; } Ok(()) diff --git a/crates/aggregator/src/threshold_plaintext_aggregator.rs b/crates/aggregator/src/threshold_plaintext_aggregator.rs index 52495d8fcc..056a0d4961 100644 --- a/crates/aggregator/src/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/threshold_plaintext_aggregator.rs @@ -4,19 +4,19 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeMap, BTreeSet}; -use crate::proof_fold::ProofFoldState; use actix::prelude::*; use anyhow::{anyhow, bail, ensure, Result}; use e3_data::Persistable; use e3_events::{ prelude::*, trap, AggregationProofPending, AggregationProofSigned, BusHandle, CircuitName, CommitteeMemberExpelled, ComputeRequest, ComputeResponse, ComputeResponseKind, CorrelationId, - DecryptedSharesAggregationProofRequest, DecryptionshareCreated, Die, E3id, EType, EnclaveEvent, - EnclaveEventData, EventContext, PartyProofsToVerify, PlaintextAggregated, Proof, ProofType, - Seed, Sequenced, ShareVerificationComplete, ShareVerificationDispatched, SignedProofFailed, - SignedProofPayload, TypedEvent, VerificationKind, ZkResponse, + DecryptedSharesAggregationProofRequest, DecryptionAggregationJobRequest, + DecryptionAggregationRequest, DecryptionshareCreated, Die, E3id, EType, EnclaveEvent, + EnclaveEventData, EventContext, PartyProofsToVerify, PlaintextAggregated, Proof, Seed, + Sequenced, ShareVerificationComplete, ShareVerificationDispatched, SignedProofPayload, + TypedEvent, VerificationKind, ZkRequest, ZkResponse, }; use e3_fhe_params::BfvPreset; use e3_sortition::{E3CommitteeContainsRequest, E3CommitteeContainsResponse, Sortition}; @@ -30,18 +30,15 @@ use e3_zk_helpers::circuits::commitments::compute_threshold_decryption_share_com use e3_zk_helpers::circuits::threshold::decrypted_shares_aggregation::MAX_MSG_NON_ZERO_COEFFS; use e3_zk_helpers::threshold::share_decryption::{Bits as C6Bits, Bounds as C6Bounds}; use e3_zk_helpers::Computation; -use tracing::{debug, error, info, trace, warn}; +use tracing::{debug, info, trace, warn}; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct Collecting { threshold_m: u64, threshold_n: u64, - shares: HashMap>, + shares: BTreeMap>, /// Signed raw C6 proofs for ShareVerification. - c6_proofs: HashMap>, - /// Wrapped C6 proofs for cross-node fold. - #[serde(default)] - c6_wrapped_proofs: HashMap>, + c6_proofs: BTreeMap>, seed: Seed, ciphertext_output: Vec, params: ArcBytes, @@ -51,10 +48,8 @@ pub struct Collecting { pub struct VerifyingC6 { threshold_m: u64, threshold_n: u64, - shares: HashMap>, - c6_proofs: HashMap>, - #[serde(default)] - c6_wrapped_proofs: HashMap>, + shares: BTreeMap>, + c6_proofs: BTreeMap>, ciphertext_output: Vec, params: ArcBytes, } @@ -162,9 +157,8 @@ impl ThresholdPlaintextAggregatorState { ThresholdPlaintextAggregatorState::Collecting(Collecting { threshold_m, threshold_n, - shares: HashMap::new(), - c6_proofs: HashMap::new(), - c6_wrapped_proofs: HashMap::new(), + shares: BTreeMap::new(), + c6_proofs: BTreeMap::new(), seed, ciphertext_output, params, @@ -177,12 +171,17 @@ pub struct ThresholdPlaintextAggregator { sortition: Addr, e3_id: E3id, params_preset: BfvPreset, + proof_aggregation_enabled: bool, state: Persistable, - /// C6 cross-node proof fold state. - c6_fold: ProofFoldState, - /// C7 proofs stored while waiting for C6 fold completion. + /// Honest parties' C6 inner proofs (sorted by party id) for [`ZkRequest::DecryptionAggregation`]. + honest_c6_proofs_for_agg: Option)>>, + /// In-flight decryption aggregation request. + decryption_aggregation_correlation: Option, + /// C7 proofs stored while waiting for decryption aggregation. c7_proofs_pending: Option>, - /// Last event context, reused for fold steps and final publish. + /// DecryptionAggregator outputs (set when ZK completes). + decryption_aggregator_proofs: Option>, + /// Last event context, reused for ZK and final publish. last_ec: Option>, } @@ -191,6 +190,7 @@ pub struct ThresholdPlaintextAggregatorParams { pub sortition: Addr, pub e3_id: E3id, pub params_preset: BfvPreset, + pub proof_aggregation_enabled: bool, } impl ThresholdPlaintextAggregator { @@ -203,9 +203,12 @@ impl ThresholdPlaintextAggregator { sortition: params.sortition, e3_id: params.e3_id, params_preset: params.params_preset, + proof_aggregation_enabled: params.proof_aggregation_enabled, state, - c6_fold: ProofFoldState::new(params.params_preset), + honest_c6_proofs_for_agg: None, + decryption_aggregation_correlation: None, c7_proofs_pending: None, + decryption_aggregator_proofs: None, last_ec: None, } } @@ -215,7 +218,6 @@ impl ThresholdPlaintextAggregator { party_id: u64, share: Vec, signed_decryption_proofs: Vec, - wrapped_proofs: Vec, ec: &EventContext, ) -> Result<()> { self.state.try_mutate(ec, |state| { @@ -227,12 +229,10 @@ impl ThresholdPlaintextAggregator { let params = current.params.clone(); let mut shares = current.shares; let mut c6_proofs = current.c6_proofs; - let mut c6_wrapped_proofs = current.c6_wrapped_proofs; info!("pushing to share collection {} {:?}", party_id, share); shares.insert(party_id, share); c6_proofs.insert(party_id, signed_decryption_proofs); - c6_wrapped_proofs.insert(party_id, wrapped_proofs); if (shares.len() as u64) < threshold_n { return Ok(ThresholdPlaintextAggregatorState::Collecting(Collecting { @@ -242,7 +242,6 @@ impl ThresholdPlaintextAggregator { ciphertext_output, shares, c6_proofs, - c6_wrapped_proofs, seed: current.seed, })); } @@ -256,7 +255,6 @@ impl ThresholdPlaintextAggregator { VerifyingC6 { shares, c6_proofs, - c6_wrapped_proofs, ciphertext_output, threshold_m, threshold_n, @@ -278,12 +276,10 @@ impl ThresholdPlaintextAggregator { let mut shares = current.shares; let mut c6_proofs = current.c6_proofs; - let mut c6_wrapped_proofs = current.c6_wrapped_proofs; let mut threshold_n = current.threshold_n; shares.remove(&party_id); c6_proofs.remove(&party_id); - c6_wrapped_proofs.remove(&party_id); if threshold_n > 0 { threshold_n -= 1; @@ -299,7 +295,6 @@ impl ThresholdPlaintextAggregator { threshold_n, shares, c6_proofs, - c6_wrapped_proofs, seed: current.seed, ciphertext_output: current.ciphertext_output, params: current.params, @@ -312,7 +307,6 @@ impl ThresholdPlaintextAggregator { threshold_n, shares, c6_proofs, - c6_wrapped_proofs, seed: current.seed, ciphertext_output: current.ciphertext_output, params: current.params, @@ -324,7 +318,6 @@ impl ThresholdPlaintextAggregator { threshold_n, shares, c6_proofs, - c6_wrapped_proofs, ciphertext_output: current.ciphertext_output, params: current.params, })) @@ -334,7 +327,7 @@ impl ThresholdPlaintextAggregator { /// Dispatch C6 proof verification through ShareVerificationActor. pub fn dispatch_c6_verification( &mut self, - c6_proofs: HashMap>, + c6_proofs: BTreeMap>, ec: EventContext, ) -> Result<()> { let party_proofs: Vec = c6_proofs @@ -432,14 +425,20 @@ impl ThresholdPlaintextAggregator { honest_shares.len(), ); - // Collect honest C6 wrapped proofs sorted by party_id for cross-node folding. - let mut honest_c6_wrapped: Vec<(u64, Vec)> = state - .c6_wrapped_proofs + // Collect honest C6 inner proofs (from signed payloads) for DecryptionAggregation. + // BTreeMap iteration yields ascending party_id, matching the slot layout + // used by honest_shares above and enforced by decryption_aggregator.nr. + let honest_c6: Vec<(u64, Vec)> = state + .c6_proofs .iter() .filter(|(id, _)| !dishonest_parties.contains(id)) - .map(|(id, proofs)| (*id, proofs.clone())) + .map(|(id, signed)| { + ( + *id, + signed.iter().map(|s| s.payload.proof.clone()).collect(), + ) + }) .collect(); - honest_c6_wrapped.sort_by_key(|(id, _)| *id); // Publish ComputeRequest before transitioning state so a publish // failure leaves us in VerifyingC6 (retryable) rather than @@ -461,6 +460,8 @@ impl ThresholdPlaintextAggregator { ); self.bus.publish(event, ec.clone())?; + self.honest_c6_proofs_for_agg = Some(honest_c6); + self.state.try_mutate(&ec, |_| { Ok(ThresholdPlaintextAggregatorState::Computing(Computing { shares: honest_shares, @@ -471,19 +472,7 @@ impl ThresholdPlaintextAggregator { })) })?; - // Start C6 cross-node fold concurrently with threshold decryption. self.last_ec = Some(ec.clone()); - let proofs: Vec = honest_c6_wrapped - .into_iter() - .flat_map(|(_, proofs)| proofs) - .collect(); - self.c6_fold.start( - proofs, - "ThresholdPlaintextAggregator C6 fold", - &self.bus, - &self.e3_id, - &ec, - )?; self.try_publish_complete()?; Ok(()) @@ -495,7 +484,7 @@ impl ThresholdPlaintextAggregator { fn verify_shares_match_c6_commitments( &self, honest_shares: &[(u64, Vec)], - c6_proofs: &HashMap>, + c6_proofs: &BTreeMap>, ) -> BTreeSet { let mut mismatched = BTreeSet::new(); @@ -506,7 +495,7 @@ impl ThresholdPlaintextAggregator { }; // Reuse the same Bounds/Bits computation that C6 codegen uses, - // so d_bit stays in sync if the formula ever changes. + // so d_native_bit stays in sync if the formula ever changes. let Ok(bounds) = C6Bounds::compute(self.params_preset, &()) else { warn!("Could not compute bounds for d_commitment check — skipping"); return mismatched; @@ -515,11 +504,10 @@ impl ThresholdPlaintextAggregator { warn!("Could not compute bits for d_commitment check — skipping"); return mismatched; }; - let d_bit = bits.d_bit; + let d_native_bit = bits.d_native_bit; let max_k = MAX_MSG_NON_ZERO_COEFFS; let c6_output_layout = CircuitName::ThresholdShareDecryption.output_layout(); - let moduli: Vec = threshold_params.moduli().to_vec(); for (party_id, shares) in honest_shares { let Some(proofs) = c6_proofs.get(party_id) else { @@ -566,21 +554,11 @@ impl ThresholdPlaintextAggregator { mismatched.insert(*party_id); continue; }; - let mut crt = e3_polynomial::CrtPolynomial::from_fhe_polynomial(&poly); - - // Apply the same transformations C6's Inputs::compute applies: - // reverse coefficient order + center each limb mod qi. - crt.reverse(); - if let Err(e) = crt.center(&moduli) { - warn!( - "Could not center d_share for party {} — marking as mismatched: {e}", - party_id - ); - mismatched.insert(*party_id); - continue; - } + let crt = e3_polynomial::CrtPolynomial::from_fhe_polynomial(&poly); - let computed = compute_threshold_decryption_share_commitment(&crt, d_bit, max_k); + // C6 public `d_commitment` hashes native truncated limbs (same layout as C7), not + // reversed+centered witness `d`. + let computed = compute_threshold_decryption_share_commitment(&crt, d_native_bit, max_k); // Convert to big-endian 32-byte padded format matching // Barretenberg's public_signals encoding. @@ -659,12 +637,85 @@ impl ThresholdPlaintextAggregator { state.plaintext.len() ); - info!("C7 proof signed — waiting for C6 cross-node fold to complete..."); + info!("C7 proof signed — awaiting DecryptionAggregation..."); self.c7_proofs_pending = Some(proofs); - self.last_ec = Some(ec); + self.last_ec = Some(ec.clone()); self.try_publish_complete() } + fn dispatch_decryption_aggregation(&mut self, ec: &EventContext) -> Result<()> { + let Some(c7_proofs) = self.c7_proofs_pending.as_ref() else { + return Ok(()); + }; + if self.decryption_aggregator_proofs.is_some() { + return Ok(()); + } + if self.decryption_aggregation_correlation.is_some() { + return Ok(()); + } + if !self.proof_aggregation_enabled { + self.decryption_aggregator_proofs = Some(Vec::new()); + return Ok(()); + } + let Some(honest_c6) = self.honest_c6_proofs_for_agg.as_ref() else { + return Ok(()); + }; + // With proof aggregation enabled we must have a complete C6 set; otherwise we'd publish + // `decryption_aggregator_proofs = Vec::new()`, which downstream consumers interpret as + // "aggregation disabled". Fail loudly instead so the missing shares are surfaced. + ensure!( + !honest_c6.is_empty() && honest_c6.iter().all(|(_, w)| !w.is_empty()), + "DecryptionAggregation: honest C6 inner proofs missing while proof aggregation is enabled" + ); + let state: GeneratingC7Proof = self + .state + .get() + .ok_or(anyhow!("Could not get state"))? + .try_into()?; + // C6Fold witness width is `T + 1` (same `T` as `threshold_m`). C7 is only proven for the + // first `T + 1` parties after sorting by party id (`handle_decrypted_shares_aggregation_proof` + // truncates); fold slot indices must stay in `0..T+1` and use that same party subset. + let c6_total_slots = state.threshold_m as usize + 1; + ensure!( + honest_c6.len() >= c6_total_slots, + "DecryptionAggregation needs at least {} honest C6 parties, have {}", + c6_total_slots, + honest_c6.len() + ); + let num_ct = c7_proofs.len(); + let mut jobs = Vec::with_capacity(num_ct); + for ct_idx in 0..num_ct { + let mut c6_inner_proofs = Vec::with_capacity(c6_total_slots); + let c6_slot_indices: Vec = (0..c6_total_slots as u32).collect(); + for (_, wps) in honest_c6.iter().take(c6_total_slots) { + let Some(p) = wps.get(ct_idx) else { + bail!("C6 inner proof missing for party at ct index {}", ct_idx); + }; + c6_inner_proofs.push(p.clone()); + } + jobs.push(DecryptionAggregationJobRequest { + c6_inner_proofs, + c6_slot_indices, + c7_proof: c7_proofs[ct_idx].clone(), + }); + } + let corr = CorrelationId::new(); + self.bus.publish( + ComputeRequest::zk( + ZkRequest::DecryptionAggregation(DecryptionAggregationRequest { + c6_total_slots, + jobs, + params_preset: self.params_preset, + }), + corr, + self.e3_id.clone(), + ), + ec.clone(), + )?; + self.decryption_aggregation_correlation = Some(corr); + Ok(()) + } + pub fn handle_compute_response(&mut self, msg: TypedEvent) -> Result<()> { let (msg, ec) = msg.into_components(); ensure!( @@ -713,20 +764,20 @@ impl ThresholdPlaintextAggregator { })?; } - // C6 cross-node fold response (ignore unrelated FoldProofs, e.g. PK C5 fold on same bus) - ComputeResponseKind::Zk(ZkResponse::FoldProofs(resp)) => { - if self.c6_fold.awaits_correlation(&correlation_id) { - let fold_ec = self.last_ec.clone().unwrap_or_else(|| ec.clone()); - if self.c6_fold.handle_response( - &correlation_id, - resp.proof, - "ThresholdPlaintextAggregator C6 fold", - &self.bus, - &self.e3_id, - &fold_ec, - )? { - self.try_publish_complete()?; + ComputeResponseKind::Zk(ZkResponse::DecryptionAggregation(resp)) => { + if self.decryption_aggregation_correlation.as_ref() == Some(&correlation_id) { + self.decryption_aggregation_correlation = None; + // Worker must return one DecryptionAggregator proof per pending C7 ciphertext. + if let Some(c7_proofs) = self.c7_proofs_pending.as_ref() { + ensure!( + resp.proofs.len() == c7_proofs.len(), + "DecryptionAggregation response proof count {} != expected {}", + resp.proofs.len(), + c7_proofs.len() + ); } + self.decryption_aggregator_proofs = Some(resp.proofs); + self.try_publish_complete()?; } } @@ -738,13 +789,17 @@ impl ThresholdPlaintextAggregator { Ok(()) } - /// Publish `PlaintextAggregated` when both C7 proofs and C6 fold are complete. + /// Publish `PlaintextAggregated` when both C7 proofs and decryption aggregation are complete. fn try_publish_complete(&mut self) -> Result<()> { - let Some(c7_proofs) = self.c7_proofs_pending.as_ref() else { + let Some(c7_proofs) = self.c7_proofs_pending.clone() else { return Ok(()); }; - let c6_ready = self.c6_fold.result.is_some() || self.c6_fold.fold_input_was_empty; - if !c6_ready { + if let Some(ec) = self.last_ec.clone() { + self.dispatch_decryption_aggregation(&ec)?; + } + let dec_ready = self.decryption_aggregator_proofs.is_some() + && self.decryption_aggregation_correlation.is_none(); + if !dec_ready { return Ok(()); } @@ -759,7 +814,7 @@ impl ThresholdPlaintextAggregator { .clone() .ok_or_else(|| anyhow!("No EventContext for publish"))?; - info!("Both C7 and C6 fold proof ready — publishing PlaintextAggregated"); + info!("C7 + decryption_aggregator proofs ready — publishing PlaintextAggregated"); let len = MAX_MSG_NON_ZERO_COEFFS * 8; let decrypted_output: Vec = state @@ -776,17 +831,19 @@ impl ThresholdPlaintextAggregator { }) .collect(); + let decryption_aggregator_proofs = self + .decryption_aggregator_proofs + .clone() + .unwrap_or_default(); + // Keep c7_proofs for invariant check; they are subsumed by the decryption_aggregator proof. + let _ = c7_proofs; let event = PlaintextAggregated { decrypted_output, e3_id: self.e3_id.clone(), - aggregation_proofs: c7_proofs.clone(), - c6_aggregated_proof: self.c6_fold.result.clone(), + decryption_aggregator_proofs, }; - info!( - "Dispatching plaintext event with C7 and C6 proofs {:?}", - event - ); + info!("Dispatching plaintext event {:?}", event); self.bus.publish(event, ec.clone())?; self.state.try_mutate(&ec, |_| { @@ -888,19 +945,12 @@ impl Handler>> party_id, decryption_share, signed_decryption_proofs, - wrapped_proofs, .. }, ec, ) = msg.into_inner().into_components(); - self.add_share( - party_id, - decryption_share, - signed_decryption_proofs, - wrapped_proofs, - &ec, - )?; + self.add_share(party_id, decryption_share, signed_decryption_proofs, &ec)?; // If we transitioned to VerifyingC6, dispatch C6 verification // using the proofs persisted in state diff --git a/crates/dashboard/src/dashboard.html b/crates/dashboard/src/dashboard.html index a5ee6f155e..12a24a440f 100644 --- a/crates/dashboard/src/dashboard.html +++ b/crates/dashboard/src/dashboard.html @@ -1,524 +1,707 @@ - + - - - -Enclave Node Dashboard - - - -
-
-

Enclave Node Dashboard

- -
-
-
Overview
-
Events
-
Status
-
-
- -
-
-

Node Information

-
-
Name...
-
Address...
-
Peer ID...
-
QUIC Port...
-
Ctrl Port...
-
+ + + + Enclave Node Dashboard + + + +
+
+

Enclave Node Dashboard

+
-
-

Noir Prover

-
Loading...
+
+
Overview
+
Events
+
Status
-
+
+ +
+
+

Node Information

+
+
Name...
+
Address...
+
Peer ID...
+
QUIC Port...
+
Ctrl Port...
+
+
+
+

Noir Prover

+
Loading...
+
+
- -
-
- - - - - - - -
-
- - - -
SeqTimestampTypeE3 IDSource
-
- -
+ +
+
+ + + + + + + +
+
+ + + + + + + + + + + +
SeqTimestampTypeE3 IDSource
+
+ +
- -
-
-

Ciphernode Status

-
Loading...
-
-
-

Wallet

-
Loading...
+ +
+
+

Ciphernode Status

+
Loading...
+
+
+

Wallet

+
Loading...
+
+
-
-
- - + // Init + loadOverview() + loadEvents() + loadStatus() + overviewTimer = setInterval(loadOverview, 10000) + setInterval(loadStatus, 30000) + + diff --git a/crates/events/src/enclave_event/compute_request/mod.rs b/crates/events/src/enclave_event/compute_request/mod.rs index c7b32f1e7b..9a76a88f6b 100644 --- a/crates/events/src/enclave_event/compute_request/mod.rs +++ b/crates/events/src/enclave_event/compute_request/mod.rs @@ -91,7 +91,9 @@ impl ToString for ComputeRequest { ZkRequest::PkAggregation(_) => "ZkPkAggregation", ZkRequest::ThresholdShareDecryption(_) => "ZkThresholdShareDecryption", ZkRequest::DecryptedSharesAggregation(_) => "ZkDecryptedSharesAggregation", - ZkRequest::FoldProofs { .. } => "ZkFoldProofs", + ZkRequest::NodeDkgFold(_) => "ZkNodeDkgFold", + ZkRequest::DkgAggregation(_) => "ZkDkgAggregation", + ZkRequest::DecryptionAggregation(_) => "ZkDecryptionAggregation", }, } .to_string() diff --git a/crates/events/src/enclave_event/compute_request/zk.rs b/crates/events/src/enclave_event/compute_request/zk.rs index 7f92598053..bb926aa321 100644 --- a/crates/events/src/enclave_event/compute_request/zk.rs +++ b/crates/events/src/enclave_event/compute_request/zk.rs @@ -36,14 +36,55 @@ pub enum ZkRequest { ThresholdShareDecryption(ThresholdShareDecryptionProofRequest), /// Generate proof for decrypted shares aggregation (C7). DecryptedSharesAggregation(DecryptedSharesAggregationProofRequest), - /// Fold two proofs into one (incremental recursive aggregation). - /// When `target_evm` is true, the output proof is generated for on-chain EVM verification. - FoldProofs { - proof1: Proof, - proof2: Proof, - target_evm: bool, - params_preset: BfvPreset, - }, + /// Per-node DKG recursive fold (C2abFold → … → NodeFold). + NodeDkgFold(NodeDkgFoldRequest), + /// Cross-node DKG aggregator (NodesFold + C5 + DkgAggregator). + DkgAggregation(DkgAggregationRequest), + /// Phase-7 decryption aggregator (C6Fold + C7 + DecryptionAggregator). + DecryptionAggregation(DecryptionAggregationRequest), +} + +/// Inputs for a single ciphertext index inside [`ZkRequest::DecryptionAggregation`]. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct DecryptionAggregationJobRequest { + pub c6_inner_proofs: Vec, + pub c6_slot_indices: Vec, + pub c7_proof: Proof, +} + +/// Full per-node DKG fold (all inner recursive proofs for one party). +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct NodeDkgFoldRequest { + pub c0_proof: Proof, + pub c1_proof: Proof, + pub c2a_proof: Proof, + pub c2b_proof: Proof, + pub c3a_inner_proofs: Vec, + pub c3b_inner_proofs: Vec, + pub c4a_proof: Proof, + pub c4b_proof: Proof, + pub c3_slot_indices_a: Vec, + pub c3_slot_indices_b: Vec, + pub c3_total_slots: usize, + pub party_id: u64, + pub params_preset: BfvPreset, +} + +/// Cross-node DKG aggregation (NodesFold + C5 + DkgAggregator). +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct DkgAggregationRequest { + pub node_fold_proofs: Vec, + pub c5_proof: Proof, + pub party_ids: Vec, + pub params_preset: BfvPreset, +} + +/// Decryption aggregation: sequential C6 fold + DecryptionAggregator per job. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct DecryptionAggregationRequest { + pub c6_total_slots: usize, + pub jobs: Vec, + pub params_preset: BfvPreset, } /// Request to generate a proof for public key aggregation (C5). @@ -112,6 +153,17 @@ pub struct ShareEncryptionProofRequest { pub esi_index: usize, } +impl ShareEncryptionProofRequest { + /// Slot index used by the C3 fold accumulator: `recipient_party_id * n_moduli + row_index`. + /// + /// This is a protocol invariant shared between the proof-request producer and the C3 fold + /// driver; it is defined here (next to the request type) so all callers reference one + /// formula. + pub fn c3_slot_index(&self, n_moduli: usize) -> u32 { + (self.recipient_party_id as u32) * (n_moduli as u32) + (self.row_index as u32) + } +} + /// Request to generate a proof for DKG share decryption (C4a or C4b). /// /// Proves that a node correctly decrypted H honest parties' BFV-encrypted @@ -215,8 +267,30 @@ pub enum ZkResponse { ThresholdShareDecryption(ThresholdShareDecryptionProofResponse), /// Proof for decrypted shares aggregation (C7). DecryptedSharesAggregation(DecryptedSharesAggregationProofResponse), - /// Folded proof (output of two-proof fold). - FoldProofs(FoldProofsResponse), + /// Output of [`ZkRequest::NodeDkgFold`]. + NodeDkgFold(NodeDkgFoldResponse), + /// Output of [`ZkRequest::DkgAggregation`]. + DkgAggregation(DkgAggregationResponse), + /// Output of [`ZkRequest::DecryptionAggregation`]. + DecryptionAggregation(DecryptionAggregationResponse), +} + +/// Response from [`ZkRequest::NodeDkgFold`]. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct NodeDkgFoldResponse { + pub proof: Proof, +} + +/// Response from [`ZkRequest::DkgAggregation`]. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct DkgAggregationResponse { + pub proof: Proof, +} + +/// Response from [`ZkRequest::DecryptionAggregation`]. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct DecryptionAggregationResponse { + pub proofs: Vec, } /// Response containing a generated proof for public key aggregation (C5). @@ -242,29 +316,19 @@ pub struct ThresholdShareDecryptionProofRequest { pub d_share_bytes: Vec, /// BFV preset for parameter resolution. pub params_preset: BfvPreset, - /// When false, skip wrapper proofs for recursive C6 folding (mirrors DKG `proof_aggregation_enabled`). - #[serde(default = "default_proof_aggregation_enabled")] - pub proof_aggregation_enabled: bool, -} - -fn default_proof_aggregation_enabled() -> bool { - true } /// Response containing generated proofs for threshold share decryption (C6). #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ThresholdShareDecryptionProofResponse { - /// One raw C6 proof per ciphertext index. + /// One C6 proof per ciphertext index. pub proofs: Vec, - /// Wrapper proof for each C6 proof (same order as `proofs`), ready for recursive folding. - pub wrapped_proofs: Vec, } /// Response containing a generated share computation proof. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ShareComputationProofResponse { pub proof: Proof, - pub wrapped_proof: Proof, pub dkg_input_type: DkgInputType, } @@ -272,7 +336,6 @@ pub struct ShareComputationProofResponse { #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ShareEncryptionProofResponse { pub proof: Proof, - pub wrapped_proof: Proof, pub dkg_input_type: DkgInputType, pub recipient_party_id: usize, pub row_index: usize, @@ -284,59 +347,48 @@ pub struct ShareEncryptionProofResponse { #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct PkBfvProofResponse { pub proof: Proof, - pub wrapped_proof: Proof, } /// Response containing a generated PK generation proof. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct PkGenerationProofResponse { pub proof: Proof, - pub wrapped_proof: Proof, } /// Response containing a generated DKG share decryption proof. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct DkgShareDecryptionProofResponse { pub proof: Proof, - pub wrapped_proof: Proof, pub dkg_input_type: DkgInputType, } impl DkgShareDecryptionProofResponse { - pub fn new(proof: Proof, wrapped_proof: Proof, dkg_input_type: DkgInputType) -> Self { + pub fn new(proof: Proof, dkg_input_type: DkgInputType) -> Self { Self { proof, - wrapped_proof, dkg_input_type, } } } impl ShareComputationProofResponse { - pub fn new(proof: Proof, wrapped_proof: Proof, dkg_input_type: DkgInputType) -> Self { + pub fn new(proof: Proof, dkg_input_type: DkgInputType) -> Self { Self { proof, - wrapped_proof, dkg_input_type, } } } impl PkBfvProofResponse { - pub fn new(proof: Proof, wrapped_proof: Proof) -> Self { - Self { - proof, - wrapped_proof, - } + pub fn new(proof: Proof) -> Self { + Self { proof } } } impl PkGenerationProofResponse { - pub fn new(proof: Proof, wrapped_proof: Proof) -> Self { - Self { - proof, - wrapped_proof, - } + pub fn new(proof: Proof) -> Self { + Self { proof } } } @@ -436,12 +488,6 @@ pub struct DecryptedSharesAggregationProofResponse { pub proofs: Vec, } -/// Response containing the folded proof. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct FoldProofsResponse { - pub proof: Proof, -} - /// ZK-specific error variants. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum ZkError { diff --git a/crates/events/src/enclave_event/decryptionshare_created.rs b/crates/events/src/enclave_event/decryptionshare_created.rs index b959096ba7..f209d082ef 100644 --- a/crates/events/src/enclave_event/decryptionshare_created.rs +++ b/crates/events/src/enclave_event/decryptionshare_created.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::{E3id, Proof, SignedProofPayload}; +use crate::{E3id, SignedProofPayload}; use actix::Message; use e3_utils::utility_types::ArcBytes; use serde::{Deserialize, Serialize}; @@ -21,9 +21,6 @@ pub struct DecryptionshareCreated { /// C6 raw proofs (signed): one per ciphertext index, used for ShareVerification. #[serde(default)] pub signed_decryption_proofs: Vec, - /// C6 wrapped proofs: one per ciphertext index, used for cross-node fold. - #[serde(default)] - pub wrapped_proofs: Vec, } impl Display for DecryptionshareCreated { diff --git a/crates/events/src/enclave_event/dkg_inner_proof_ready.rs b/crates/events/src/enclave_event/dkg_inner_proof_ready.rs index 5d257ad388..1a1e324807 100644 --- a/crates/events/src/enclave_event/dkg_inner_proof_ready.rs +++ b/crates/events/src/enclave_event/dkg_inner_proof_ready.rs @@ -12,10 +12,9 @@ use crate::{E3id, Proof}; use serde::{Deserialize, Serialize}; -/// A single wrapped inner proof ready for incremental aggregation. +/// A single inner DKG proof ready for incremental aggregation (`NodeFold` input chain). /// -/// Emitted for every inner circuit (C0-C4) as soon as its wrapped proof -/// is available. `seq` gives the deterministic ordering. +/// Emitted for every inner circuit (C0–C4) when available. `seq` gives the deterministic ordering. /// /// The total count of expected proofs is communicated separately via /// [`ThresholdSharePending`], which is always published before the first @@ -24,8 +23,7 @@ use serde::{Deserialize, Serialize}; pub struct DKGInnerProofReady { pub e3_id: E3id, pub party_id: u64, - /// Already-wrapped proof (single-element RecursiveAggregation output). - pub wrapped_proof: Proof, + pub proof: Proof, /// Deterministic sequence index for ordered folding. pub seq: usize, } diff --git a/crates/events/src/enclave_event/keyshare_created.rs b/crates/events/src/enclave_event/keyshare_created.rs index 19795149d0..9ade33d4a5 100644 --- a/crates/events/src/enclave_event/keyshare_created.rs +++ b/crates/events/src/enclave_event/keyshare_created.rs @@ -19,6 +19,9 @@ pub struct KeyshareCreated { pub pubkey: ArcBytes, pub e3_id: E3id, pub node: String, + /// Real sortition-assigned party id. Required (no `serde(default)`): a missing value + /// would silently default to 0 and mis-route shares. + pub party_id: u64, #[serde(default)] pub signed_pk_generation_proof: Option, } diff --git a/crates/events/src/enclave_event/plaintext_aggregated.rs b/crates/events/src/enclave_event/plaintext_aggregated.rs index aa77de0a25..dc5b0f4ce1 100644 --- a/crates/events/src/enclave_event/plaintext_aggregated.rs +++ b/crates/events/src/enclave_event/plaintext_aggregated.rs @@ -17,12 +17,12 @@ use std::fmt::{self, Display}; pub struct PlaintextAggregated { pub e3_id: E3id, pub decrypted_output: Vec, - /// C7 proofs: one proof of correct aggregation per ciphertext index. + /// Final DecryptionAggregator (EVM) proof(s): one per ciphertext index. Empty when + /// proof aggregation is disabled. On-chain publication currently only supports a + /// single-output plaintext, in which case the first proof is forwarded to + /// `publishPlaintextOutput`. #[serde(default)] - pub aggregation_proofs: Vec, - /// Cross-node folded C6 proof: all honest nodes' threshold share decryption proofs folded into one. - #[serde(default)] - pub c6_aggregated_proof: Option, + pub decryption_aggregator_proofs: Vec, } impl Display for PlaintextAggregated { diff --git a/crates/events/src/enclave_event/proof.rs b/crates/events/src/enclave_event/proof.rs index bca888361b..a53d41d3d9 100644 --- a/crates/events/src/enclave_event/proof.rs +++ b/crates/events/src/enclave_event/proof.rs @@ -4,7 +4,7 @@ use derivative::Derivative; use e3_utils::utility_types::ArcBytes; use e3_zk_helpers::{ CircuitInputLayout, CircuitOutputLayout, DKG_SHARE_DECRYPTION_OUTPUTS, PK_AGGREGATION_OUTPUTS, - PK_BFV_OUTPUTS, PK_GENERATION_OUTPUTS, SHARE_ENCRYPTION_INPUTS, + PK_BFV_OUTPUTS, PK_GENERATION_OUTPUTS, SHARE_ENCRYPTION_INPUTS, SHARE_ENCRYPTION_OUTPUTS, THRESHOLD_SHARE_DECRYPTION_INPUTS, THRESHOLD_SHARE_DECRYPTION_OUTPUTS, }; use serde::{Deserialize, Serialize}; @@ -114,8 +114,6 @@ pub enum CircuitName { SkShareComputation, /// E_SM share computation inner proof (C2b, recursive). ESmShareComputation, - /// Share computation wrapper proof (verifies one inner C2 proof; fold input). - ShareComputation, /// Share encryption proof (C3). ShareEncryption, /// DKG share decryption proof (C4). @@ -126,8 +124,30 @@ pub enum CircuitName { ThresholdShareDecryption, /// Decrypted shares aggregation proof (C7). DecryptedSharesAggregation, - /// Recursive aggregation fold circuit (independent; lives at recursive_aggregation/fold). - Fold, + /// Sequential C3 fold: inner ZK + optional prior `c3_fold` non-ZK proof. + C3Fold, + /// Bootstrap circuit for [`CircuitName::C3Fold`] genesis accumulator proof (same ABI, no acc verify). + C3FoldKernel, + /// Sequential C6 fold: inner ZK + prior `c6_fold` non-ZK proof (phase-7 aggregator). + C6Fold, + /// Bootstrap circuit for [`CircuitName::C6Fold`] genesis accumulator proof (same ABI, no acc verify). + C6FoldKernel, + /// Ad-hoc recursive aggregation: C2a + C2b. + C2abFold, + /// Ad-hoc: final sk `c3_fold` + final e_sm `c3_fold`. + C3abFold, + /// Ad-hoc: C4a + C4b. + C4abFold, + /// Per-node DKG fold (C0..C4 links). + NodeFold, + /// Sequential fold of `H` `node_fold` proofs (non-ZK) before `dkg_aggregator`. + NodesFold, + /// Bootstrap circuit for [`CircuitName::NodesFold`] genesis accumulator proof (same ABI, no acc verify). + NodesFoldKernel, + /// DKG aggregator (folded `node_fold` via `nodes_fold` + C5). + DkgAggregator, + /// Phase-7 decryption aggregator (folded C6 via `c6_fold` + C7). + DecryptionAggregator, } impl CircuitName { @@ -137,13 +157,23 @@ impl CircuitName { CircuitName::PkGeneration => "pk_generation", CircuitName::SkShareComputation => "sk_share_computation", CircuitName::ESmShareComputation => "e_sm_share_computation", - CircuitName::ShareComputation => "share_computation", CircuitName::ShareEncryption => "share_encryption", CircuitName::DkgShareDecryption => "share_decryption", CircuitName::PkAggregation => "pk_aggregation", CircuitName::ThresholdShareDecryption => "share_decryption", CircuitName::DecryptedSharesAggregation => "decrypted_shares_aggregation", - CircuitName::Fold => "fold", + CircuitName::C3Fold => "c3_fold", + CircuitName::C3FoldKernel => "c3_fold_kernel", + CircuitName::C6Fold => "c6_fold", + CircuitName::C6FoldKernel => "c6_fold_kernel", + CircuitName::C2abFold => "c2ab_fold", + CircuitName::C3abFold => "c3ab_fold", + CircuitName::C4abFold => "c4ab_fold", + CircuitName::NodeFold => "node_fold", + CircuitName::NodesFold => "nodes_fold", + CircuitName::NodesFoldKernel => "nodes_fold_kernel", + CircuitName::DkgAggregator => "dkg_aggregator", + CircuitName::DecryptionAggregator => "decryption_aggregator", } } @@ -152,14 +182,24 @@ impl CircuitName { CircuitName::PkBfv => "dkg", CircuitName::SkShareComputation => "dkg", CircuitName::ESmShareComputation => "dkg", - CircuitName::ShareComputation => "dkg", CircuitName::ShareEncryption => "dkg", CircuitName::DkgShareDecryption => "dkg", CircuitName::PkGeneration => "threshold", CircuitName::ThresholdShareDecryption => "threshold", CircuitName::PkAggregation => "threshold", CircuitName::DecryptedSharesAggregation => "threshold", - CircuitName::Fold => "recursive_aggregation", + CircuitName::C3Fold + | CircuitName::C3FoldKernel + | CircuitName::C6Fold + | CircuitName::C6FoldKernel + | CircuitName::C2abFold + | CircuitName::C3abFold + | CircuitName::C4abFold + | CircuitName::NodeFold + | CircuitName::NodesFold + | CircuitName::NodesFoldKernel + | CircuitName::DkgAggregator + | CircuitName::DecryptionAggregator => "recursive_aggregation", } } @@ -167,24 +207,6 @@ impl CircuitName { format!("{}/{}", self.group(), self.as_str()) } - /// Wrapper circuit path: `recursive_aggregation/wrapper/{group}/{name}`. - pub fn wrapper_dir_path(&self) -> String { - format!("recursive_aggregation/wrapper/{}", self.dir_path()) - } - - /// Compiled wrapper bundle to use when wrapping an inner proof. - /// - /// C2a/C2b inner circuits (`sk_share_computation`, `e_sm_share_computation`) share one wrapper - /// Noir program (`share_computation` under `recursive_aggregation/wrapper/dkg/`). - pub fn wrapper_artifact_circuit(&self) -> CircuitName { - match self { - CircuitName::SkShareComputation | CircuitName::ESmShareComputation => { - CircuitName::ShareComputation - } - _ => *self, - } - } - /// Public input layout for this circuit. /// /// Public output (return value) layout for this circuit. @@ -199,7 +221,6 @@ impl CircuitName { CircuitName::SkShareComputation | CircuitName::ESmShareComputation => { CircuitOutputLayout::Dynamic } - CircuitName::ShareComputation => CircuitOutputLayout::None, CircuitName::DkgShareDecryption => CircuitOutputLayout::Fixed { fields: DKG_SHARE_DECRYPTION_OUTPUTS, }, @@ -209,9 +230,22 @@ impl CircuitName { CircuitName::ThresholdShareDecryption => CircuitOutputLayout::Fixed { fields: THRESHOLD_SHARE_DECRYPTION_OUTPUTS, }, - CircuitName::ShareEncryption => CircuitOutputLayout::None, + CircuitName::ShareEncryption => CircuitOutputLayout::Fixed { + fields: SHARE_ENCRYPTION_OUTPUTS, + }, CircuitName::DecryptedSharesAggregation => CircuitOutputLayout::None, - CircuitName::Fold => CircuitOutputLayout::None, + CircuitName::C3Fold + | CircuitName::C3FoldKernel + | CircuitName::C6Fold + | CircuitName::C6FoldKernel + | CircuitName::C2abFold + | CircuitName::C3abFold + | CircuitName::C4abFold + | CircuitName::NodeFold + | CircuitName::NodesFold + | CircuitName::NodesFoldKernel + | CircuitName::DkgAggregator + | CircuitName::DecryptionAggregator => CircuitOutputLayout::None, } } @@ -282,11 +316,12 @@ mod tests { #[test] fn extract_c6_d_commitment_after_pub_inputs() { - // C6: 2 public inputs + 1 output (`d_commitment` at tail). - let mut signals = vec![0u8; 96]; + // C6: 3 public inputs + 1 output (`d_commitment` at tail). + let mut signals = vec![0u8; 128]; signals[0..32].copy_from_slice(&[0x11; 32]); // expected_sk_commitment signals[32..64].copy_from_slice(&[0x22; 32]); // expected_e_sm_commitment - signals[64..96].copy_from_slice(&[0x77; 32]); // d_commitment + signals[64..96].copy_from_slice(&[0x33; 32]); // ct_commitment + signals[96..128].copy_from_slice(&[0x77; 32]); // d_commitment let proof = make_proof(CircuitName::ThresholdShareDecryption, &signals); assert_eq!(&*proof.extract_output("d_commitment").unwrap(), &[0x77; 32]); @@ -294,10 +329,11 @@ mod tests { #[test] fn extract_c6_public_inputs() { - let mut signals = vec![0u8; 96]; + let mut signals = vec![0u8; 128]; signals[0..32].copy_from_slice(&[0x11; 32]); signals[32..64].copy_from_slice(&[0x22; 32]); - signals[64..96].copy_from_slice(&[0x77; 32]); + signals[64..96].copy_from_slice(&[0x33; 32]); + signals[96..128].copy_from_slice(&[0x77; 32]); let proof = make_proof(CircuitName::ThresholdShareDecryption, &signals); assert_eq!( @@ -308,6 +344,7 @@ mod tests { &*proof.extract_input("expected_e_sm_commitment").unwrap(), &[0x22; 32] ); + assert_eq!(&*proof.extract_input("ct_commitment").unwrap(), &[0x33; 32]); } #[test] @@ -327,9 +364,16 @@ mod tests { } #[test] - fn extract_from_void_circuit() { - let proof = make_proof(CircuitName::ShareEncryption, &[0u8; 64]); - assert!(proof.extract_output("commitment").is_none()); + fn extract_ct_commitment_from_share_encryption() { + let mut signals = vec![0u8; 96]; + signals[0..32].copy_from_slice(&[0xAA; 32]); + signals[32..64].copy_from_slice(&[0xBB; 32]); + signals[64..96].copy_from_slice(&[0xCC; 32]); + let proof = make_proof(CircuitName::ShareEncryption, &signals); + assert_eq!( + &*proof.extract_output("ct_commitment").unwrap(), + &[0xCC; 32] + ); } #[test] @@ -362,10 +406,11 @@ mod tests { #[test] fn extract_input_from_share_encryption() { - // C3: 2 pub inputs at HEAD + rest of signals + // C3: 2 pub inputs at HEAD + ct_commitment return at tail let mut signals = vec![0u8; 96]; signals[0..32].copy_from_slice(&[0xAA; 32]); // expected_pk_commitment signals[32..64].copy_from_slice(&[0xBB; 32]); // expected_message_commitment + signals[64..96].copy_from_slice(&[0xCC; 32]); // ct_commitment let proof = make_proof(CircuitName::ShareEncryption, &signals); assert_eq!( @@ -376,6 +421,10 @@ mod tests { &*proof.extract_input("expected_message_commitment").unwrap(), &[0xBB; 32] ); + assert_eq!( + &*proof.extract_output("ct_commitment").unwrap(), + &[0xCC; 32] + ); } #[test] diff --git a/crates/events/src/enclave_event/publickey_aggregated.rs b/crates/events/src/enclave_event/publickey_aggregated.rs index 9ba9130dee..9c2d49ef8d 100644 --- a/crates/events/src/enclave_event/publickey_aggregated.rs +++ b/crates/events/src/enclave_event/publickey_aggregated.rs @@ -19,12 +19,13 @@ pub struct PublicKeyAggregated { pub pubkey: ArcBytes, // TODO: ArcBytes ? pub e3_id: E3id, pub nodes: OrderedSet, - /// C5 proof: proof of correct pk aggregation. + /// Hash-based aggregated PK commitment (last public signal of the C5 proof). + /// Passed as `pkCommitment` to `publishCommittee`. + pub pk_commitment: [u8; 32], + /// EVM DKG recursive proof (`CircuitName::DkgAggregator`) carrying node folds + C5 + /// for on-chain verification. `None` when proof aggregation is disabled. #[serde(default)] - pub pk_aggregation_proof: Option, - /// Cross-node aggregated DKG proof: all honest nodes' recursive proofs folded into one. - #[serde(default)] - pub dkg_aggregated_proof: Option, + pub dkg_aggregator_proof: Option, } impl Display for PublicKeyAggregated { diff --git a/crates/events/src/enclave_event/signed_proof.rs b/crates/events/src/enclave_event/signed_proof.rs index d05d96b8a2..af28f4ab9c 100644 --- a/crates/events/src/enclave_event/signed_proof.rs +++ b/crates/events/src/enclave_event/signed_proof.rs @@ -57,20 +57,8 @@ impl ProofType { match self { ProofType::C0PkBfv => vec![CircuitName::PkBfv], ProofType::C1PkGeneration => vec![CircuitName::PkGeneration], - // C2 proofs are signed as inner recursive circuits; the ShareComputation entry allows - // verifying a wrapped proof if we ever publish that instead. - ProofType::C2aSkShareComputation => { - vec![ - CircuitName::SkShareComputation, - CircuitName::ShareComputation, - ] - } - ProofType::C2bESmShareComputation => { - vec![ - CircuitName::ESmShareComputation, - CircuitName::ShareComputation, - ] - } + ProofType::C2aSkShareComputation => vec![CircuitName::SkShareComputation], + ProofType::C2bESmShareComputation => vec![CircuitName::ESmShareComputation], ProofType::C3aSkShareEncryption => vec![CircuitName::ShareEncryption], ProofType::C3bESmShareEncryption => vec![CircuitName::ShareEncryption], ProofType::C4aSkShareDecryption | ProofType::C4bESmShareDecryption => { @@ -417,17 +405,11 @@ mod tests { ); assert_eq!( ProofType::C2aSkShareComputation.circuit_names(), - vec![ - CircuitName::SkShareComputation, - CircuitName::ShareComputation - ] + vec![CircuitName::SkShareComputation] ); assert_eq!( ProofType::C2bESmShareComputation.circuit_names(), - vec![ - CircuitName::ESmShareComputation, - CircuitName::ShareComputation - ] + vec![CircuitName::ESmShareComputation] ); assert_eq!( ProofType::C6ThresholdShareDecryption.circuit_names(), diff --git a/crates/evm-helpers/src/contracts.rs b/crates/evm-helpers/src/contracts.rs index 8a0ae61fac..6945f19c46 100644 --- a/crates/evm-helpers/src/contracts.rs +++ b/crates/evm-helpers/src/contracts.rs @@ -127,7 +127,7 @@ sol! { function request(E3RequestParams calldata requestParams) external returns (uint256 e3Id, E3 memory e3); function enableE3Program(address e3Program) public returns (bool success); function publishCiphertextOutput(uint256 e3Id, bytes calldata ciphertextOutput, bytes calldata proof) external returns (bool success); - function publishPlaintextOutput(uint256 e3Id, bytes calldata data, bytes calldata proof, bytes calldata foldProof) external returns (bool success); + function publishPlaintextOutput(uint256 e3Id, bytes calldata data, bytes calldata proof) external returns (bool success); function getE3(uint256 e3Id) external view returns (E3 memory e3); function paramSetRegistry(uint8 paramSet) external view returns (bytes memory encodedParams); function getE3Quote(E3RequestParams memory request) external view returns (uint256 fee); @@ -211,7 +211,6 @@ pub trait EnclaveWrite { e3_id: U256, data: Bytes, proof: Bytes, - fold_proof: Bytes, ) -> Result; } @@ -510,7 +509,6 @@ impl EnclaveWrite for EnclaveContract { e3_id: U256, data: Bytes, proof: Bytes, - fold_proof: Bytes, ) -> Result { let _guard = NONCE_LOCK.lock().await; let wallet_addr = self @@ -520,7 +518,7 @@ impl EnclaveWrite for EnclaveContract { let contract = Enclave::new(self.contract_address, &self.provider); let builder = contract - .publishPlaintextOutput(e3_id, data, proof, fold_proof) + .publishPlaintextOutput(e3_id, data, proof) .nonce(nonce); let receipt = builder.send().await?.get_receipt().await?; diff --git a/crates/evm-helpers/src/events.rs b/crates/evm-helpers/src/events.rs index 0fd272e028..3ec88f5cb3 100644 --- a/crates/evm-helpers/src/events.rs +++ b/crates/evm-helpers/src/events.rs @@ -24,7 +24,7 @@ sol! { #[derive(Debug)] interface IPkVerifier { - function verify(bytes memory proof) external view returns (bytes32 pkCommitment); + function verify(bytes32 pkCommitment, bytes memory proof) external view returns (bool); } #[derive(Debug)] @@ -61,7 +61,7 @@ sol! { event PlaintextOutputPublished(uint256 indexed e3Id, bytes plaintextOutput, bytes proof); #[derive(Debug)] - event CommitteePublished(uint256 indexed e3Id, address[] nodes, bytes publicKey, bytes proof); + event CommitteePublished(uint256 indexed e3Id, address[] nodes, bytes publicKey, bytes32 pkCommitment, bytes proof); #[derive(Debug)] enum E3Stage { diff --git a/crates/evm-helpers/tests/fixtures/fake_enclave.sol b/crates/evm-helpers/tests/fixtures/fake_enclave.sol index 61e2a0cf6f..1b127d97be 100644 --- a/crates/evm-helpers/tests/fixtures/fake_enclave.sol +++ b/crates/evm-helpers/tests/fixtures/fake_enclave.sol @@ -10,7 +10,7 @@ contract FakeEnclave { event InputPublished(uint256 indexed e3Id, bytes data, uint256 inputHash, uint256 index); event CiphertextOutputPublished(uint256 indexed e3Id, bytes ciphertextOutput); event PlaintextOutputPublished(uint256 indexed e3Id, bytes plaintextOutput, bytes proof); - event CommitteePublished(uint256 indexed e3Id, address[] nodes, bytes publicKey, bytes proof); + event CommitteePublished(uint256 indexed e3Id, address[] nodes, bytes publicKey, bytes32 pkCommitment, bytes proof); mapping(uint8 => bytes) public paramSetRegistry; @@ -30,9 +30,9 @@ contract FakeEnclave { } // Emit CommitteePublished event with passed test data - function emitCommitteePublished(uint256 e3Id, bytes memory publicKey, bytes memory proof) public { + function emitCommitteePublished(uint256 e3Id, bytes memory publicKey, bytes32 pkCommitment, bytes memory proof) public { address[] memory nodes = new address[](1); - emit CommitteePublished(e3Id, nodes, publicKey, proof); + emit CommitteePublished(e3Id, nodes, publicKey, pkCommitment, proof); } function getE3(uint256 _e3Id) external view returns (E3 memory e3) { diff --git a/crates/evm/src/ciphernode_registry_sol.rs b/crates/evm/src/ciphernode_registry_sol.rs index 7c6cebae72..fc5d92f238 100644 --- a/crates/evm/src/ciphernode_registry_sol.rs +++ b/crates/evm/src/ciphernode_registry_sol.rs @@ -490,7 +490,8 @@ impl Handler Handler, public_key: ArcBytes, - pk_aggregation_proof: Option<&Proof>, - dkg_aggregated_proof: Option<&Proof>, + pk_commitment: [u8; 32], + dkg_aggregator_proof: Option<&Proof>, ) -> Result { let e3_id_u256: U256 = e3_id.try_into()?; let public_key_bytes = Bytes::from(public_key.extract_bytes()); + let pk_commitment_b256 = B256::from(pk_commitment); - let proof: Bytes = encode_zk_proof( - pk_aggregation_proof.ok_or_else(|| anyhow::anyhow!("pk_aggregation_proof required"))?, - )?; - let fold_proof: Bytes = match dkg_aggregated_proof { + // `proof` is empty when `proofAggregationEnabled = false`; the contract + // trusts `pk_commitment` directly in that case. + let proof: Bytes = match dkg_aggregator_proof { Some(p) => encode_zk_proof(p)?, None => Bytes::new(), }; @@ -695,7 +696,6 @@ pub async fn publish_committee_to_registry Handler Handler( contract_address: Address, e3_id: E3id, decrypted_output: Vec, - aggregation_proof: Option<&Proof>, - c6_fold_proof: Option<&Proof>, + decryption_aggregator_proof: Option<&Proof>, ) -> Result { let e3_id: U256 = e3_id.try_into()?; @@ -308,10 +310,8 @@ async fn publish_plaintext_output( .pending() .await?; - let proof = encode_zk_proof( - aggregation_proof.ok_or_else(|| anyhow::anyhow!("C7 proof missing or invalid"))?, - )?; - let fold_proof: Bytes = match c6_fold_proof { + // `None` => proof aggregation disabled; contract accepts empty bytes in that case. + let proof: Bytes = match decryption_aggregator_proof { Some(p) => encode_zk_proof(p)?, None => Bytes::new(), }; @@ -322,13 +322,12 @@ async fn publish_plaintext_output( || { info!("publishPlaintextOutput() e3_id={:?}", e3_id); let decrypted_output = Bytes::from(decrypted_output.clone()); - let fold_proof = fold_proof.clone(); let proof = proof.clone(); let contract = IEnclave::new(contract_address, provider.provider()); async move { let builder = contract - .publishPlaintextOutput(e3_id, decrypted_output, proof, fold_proof) + .publishPlaintextOutput(e3_id, decrypted_output, proof) .nonce(current_nonce); let receipt = builder.send().await?.get_receipt().await?; Ok(receipt) diff --git a/crates/indexer/tests/fixtures/fake_enclave.sol b/crates/indexer/tests/fixtures/fake_enclave.sol index 0c306441e4..004599e965 100644 --- a/crates/indexer/tests/fixtures/fake_enclave.sol +++ b/crates/indexer/tests/fixtures/fake_enclave.sol @@ -10,7 +10,7 @@ contract FakeEnclave { event InputPublished(uint256 indexed e3Id, bytes data, uint256 inputHash, uint256 index); event CiphertextOutputPublished(uint256 indexed e3Id, bytes ciphertextOutput); event PlaintextOutputPublished(uint256 indexed e3Id, bytes plaintextOutput, bytes proof); - event CommitteePublished(uint256 indexed e3Id, address[] nodes, bytes publicKey, bytes proof); + event CommitteePublished(uint256 indexed e3Id, address[] nodes, bytes publicKey, bytes32 pkCommitment, bytes proof); mapping(uint8 => bytes) public paramSetRegistry; @@ -30,9 +30,9 @@ contract FakeEnclave { } // Emit CommitteePublished event with passed test data - function emitCommitteePublished(uint256 e3Id, bytes memory publicKey, bytes memory proof) public { + function emitCommitteePublished(uint256 e3Id, bytes memory publicKey, bytes32 pkCommitment, bytes memory proof) public { address[] memory nodes = new address[](1); - emit CommitteePublished(e3Id, nodes, publicKey, proof); + emit CommitteePublished(e3Id, nodes, publicKey, pkCommitment, proof); } function getE3(uint256 _e3Id) external view returns (E3 memory e3) { diff --git a/crates/indexer/tests/integration.rs b/crates/indexer/tests/integration.rs index fa32b6e142..d995267bda 100644 --- a/crates/indexer/tests/integration.rs +++ b/crates/indexer/tests/integration.rs @@ -100,7 +100,7 @@ async fn test_indexer() -> Result<()> { let sk = SecretKey::random(¶ms, &mut rng); let pk = PublicKey::new(&sk, &mut rng); - _ = compute_pk_commitment( + let pk_commitment = compute_pk_commitment( pk.to_bytes(), params.degree(), params.plaintext(), @@ -116,6 +116,7 @@ async fn test_indexer() -> Result<()> { .emitCommitteePublished( Uint::from(E3_ID), Bytes::from(pk.to_bytes()), + pk_commitment.into(), Bytes::default(), ) .send() diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index cf8dc401de..99afe0fd6b 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -2061,6 +2061,7 @@ impl ThresholdKeyshare { let state = self.state.try_get()?; let e3_id = state.get_e3_id(); let address = state.get_address().to_owned(); + let party_id = state.get_party_id(); let current: ReadyForDecryption = state.clone().try_into()?; info!("Publishing Exchange #4 (KeyshareCreated) for E3 {}", e3_id); @@ -2070,6 +2071,7 @@ impl ThresholdKeyshare { pubkey: current.pk_share, e3_id: e3_id.clone(), node: address, + party_id, signed_pk_generation_proof: current.signed_pk_generation_proof, }, ec, @@ -2176,7 +2178,6 @@ impl ThresholdKeyshare { es_poly_sum: decrypting.es_poly_sum, d_share_bytes: d_share_poly.clone(), params_preset: threshold_preset, - proof_aggregation_enabled: state.proof_aggregation_enabled, }, }, ec.clone(), diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 823ea86ca0..4ee356b4f4 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -23,14 +23,16 @@ use e3_events::trap_fut; use e3_events::EType; use e3_events::EffectsEnabled; use e3_events::{ - BusHandle, CircuitName, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, - ComputeRequestKind, ComputeResponse, DecryptedSharesAggregationProofRequest, - DecryptedSharesAggregationProofResponse, DkgShareDecryptionProofRequest, - DkgShareDecryptionProofResponse, EnclaveEvent, EnclaveEventData, EventPublisher, - EventSubscriber, EventType, FoldProofsResponse, PartyVerificationResult, - PkAggregationProofRequest, PkAggregationProofResponse, PkBfvProofRequest, PkBfvProofResponse, - PkGenerationProofRequest, PkGenerationProofResponse, Proof, ShareComputationProofRequest, - ShareComputationProofResponse, ShareEncryptionProofRequest, ShareEncryptionProofResponse, + BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeRequestKind, + ComputeResponse, DecryptedSharesAggregationProofRequest, + DecryptedSharesAggregationProofResponse, DecryptionAggregationRequest, + DecryptionAggregationResponse, DkgAggregationRequest, DkgAggregationResponse, + DkgShareDecryptionProofRequest, DkgShareDecryptionProofResponse, EnclaveEvent, + EnclaveEventData, EventPublisher, EventSubscriber, EventType, NodeDkgFoldRequest, + NodeDkgFoldResponse, PartyVerificationResult, PkAggregationProofRequest, + PkAggregationProofResponse, PkBfvProofRequest, PkBfvProofResponse, PkGenerationProofRequest, + PkGenerationProofResponse, Proof, ShareComputationProofRequest, ShareComputationProofResponse, + ShareEncryptionProofRequest, ShareEncryptionProofResponse, ThresholdShareDecryptionProofRequest, ThresholdShareDecryptionProofResponse, TypedEvent, VerifyShareDecryptionProofsRequest, VerifyShareDecryptionProofsResponse, VerifyShareProofsRequest, VerifyShareProofsResponse, ZkError as ZkEventError, ZkRequest, @@ -67,9 +69,9 @@ use e3_zk_helpers::dkg::share_encryption::{ShareEncryptionCircuit, ShareEncrypti use e3_zk_helpers::threshold::pk_aggregation::PkAggregationCircuit; use e3_zk_helpers::threshold::pk_aggregation::PkAggregationCircuitData; use e3_zk_helpers::CiphernodesCommittee; -use e3_zk_helpers::Computation; use e3_zk_prover::{ - generate_fold_proof, generate_wrapper_proof, CircuitVariant, Provable, ZkBackend, ZkError, + prove_decryption_aggregation_jobs, prove_dkg_aggregation, prove_node_dkg_fold, CircuitVariant, + DecryptionAggregationJob, DkgAggregationInput, NodeDkgFoldInput, Provable, ZkBackend, ZkError, ZkProver, }; use fhe::bfv::{Ciphertext, Encoding, Plaintext, PublicKey, SecretKey}; @@ -79,7 +81,7 @@ use ndarray::Array2; use num_bigint::BigInt; use rand::rngs::OsRng; use rand::Rng; -use tracing::{error, info, warn}; +use tracing::{error, info}; /// Multithread actor pub struct Multithread { @@ -357,7 +359,8 @@ fn handle_pk_aggregation_proof( // dispatching this request (pre-aggregation check). By the time we reach // the prover, all keyshares are guaranteed to match their C1 proofs. - // 7. Generate proof via Provable trait (C5 is always EVM-targeted for on-chain verification) + // 7. C5 uses noir-recursive-no-zk (non-ZK recursive); it is verified inside `DkgAggregator` via + // `verify_honk_proof_non_zk`. The EVM-facing proof for on-chain is `CircuitName::DkgAggregator`. let circuit = PkAggregationCircuit; let bb_work_id = zk_bb_work_id(&request); let artifacts_dir = req.params_preset.artifacts_dir(); @@ -367,7 +370,7 @@ fn handle_pk_aggregation_proof( &req.params_preset, &circuit_data, &bb_work_id, - CircuitVariant::Evm, + CircuitVariant::Default, &artifacts_dir, ) .map_err(|e| { @@ -420,7 +423,6 @@ fn handle_threshold_share_decryption_proof( )); } let mut proofs = Vec::with_capacity(num_indices); - let mut wrapped_proofs = Vec::with_capacity(num_indices); let bb_work_base = zk_bb_work_id(&request); let artifacts_dir = req.params_preset.artifacts_dir(); @@ -478,28 +480,11 @@ fn handle_threshold_share_decryption_proof( ) })?; - if req.proof_aggregation_enabled { - let wrap_id = format!("{bb_work_base}_c6_{i}_w"); - let wrapped = generate_wrapper_proof(prover, &proof, &wrap_id, &artifacts_dir) - .map_err(|e| { - ComputeRequestError::new( - ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(format!( - "C6 wrapper proof[{}]: {}", - i, e - ))), - request.clone(), - ) - })?; - wrapped_proofs.push(wrapped); - } proofs.push(proof); } Ok(ComputeResponse::zk( - ZkResponse::ThresholdShareDecryption(ThresholdShareDecryptionProofResponse { - proofs, - wrapped_proofs, - }), + ZkResponse::ThresholdShareDecryption(ThresholdShareDecryptionProofResponse { proofs }), request.correlation_id, request.e3_id, )) @@ -691,36 +676,113 @@ fn handle_zk_request( handle_decrypted_shares_aggregation_proof(&prover, req, request.clone()) }) } - ZkRequest::FoldProofs { - proof1, - proof2, - target_evm, - params_preset, - } => timefunc("zk_fold_proofs", id, || { - let bb_work_id = zk_bb_work_id(&request); - let artifacts_dir = params_preset.artifacts_dir(); - match generate_fold_proof( - &prover, - &proof1, - &proof2, - &bb_work_id, - target_evm, - &artifacts_dir, - ) { - Ok(proof) => Ok(ComputeResponse::zk( - ZkResponse::FoldProofs(FoldProofsResponse { proof }), - request.correlation_id, - request.e3_id.clone(), - )), - Err(e) => Err(ComputeRequestError::new( - ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), - request.clone(), - )), - } + ZkRequest::NodeDkgFold(req) => timefunc("zk_node_dkg_fold", id, || { + handle_node_dkg_fold_proof(&prover, req, request.clone()) + }), + ZkRequest::DkgAggregation(req) => timefunc("zk_dkg_aggregation", id, || { + handle_dkg_aggregation_proof(&prover, req, request.clone()) + }), + ZkRequest::DecryptionAggregation(req) => timefunc("zk_decryption_aggregation", id, || { + handle_decryption_aggregation_proof(&prover, req, request.clone()) }), } } +fn handle_node_dkg_fold_proof( + prover: &ZkProver, + req: NodeDkgFoldRequest, + request: ComputeRequest, +) -> Result { + let artifacts_dir = req.params_preset.artifacts_dir(); + let job_id = zk_bb_work_id(&request); + let input = NodeDkgFoldInput { + c0_proof: &req.c0_proof, + c1_proof: &req.c1_proof, + c2a_proof: &req.c2a_proof, + c2b_proof: &req.c2b_proof, + c3a_inner_proofs: &req.c3a_inner_proofs, + c3b_inner_proofs: &req.c3b_inner_proofs, + c3_slot_indices_a: &req.c3_slot_indices_a, + c3_slot_indices_b: &req.c3_slot_indices_b, + c3_total_slots: req.c3_total_slots, + c4a_proof: &req.c4a_proof, + c4b_proof: &req.c4b_proof, + party_id: req.party_id, + }; + let proof = prove_node_dkg_fold(prover, &input, &job_id, &artifacts_dir).map_err(|e| { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), + request.clone(), + ) + })?; + Ok(ComputeResponse::zk( + ZkResponse::NodeDkgFold(NodeDkgFoldResponse { proof }), + request.correlation_id, + request.e3_id, + )) +} + +fn handle_dkg_aggregation_proof( + prover: &ZkProver, + req: DkgAggregationRequest, + request: ComputeRequest, +) -> Result { + let artifacts_dir = req.params_preset.artifacts_dir(); + let job_id = zk_bb_work_id(&request); + let input = DkgAggregationInput { + node_fold_proofs: &req.node_fold_proofs, + c5_proof: &req.c5_proof, + party_ids: &req.party_ids, + }; + let proof = prove_dkg_aggregation(prover, &input, &job_id, &artifacts_dir).map_err(|e| { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), + request.clone(), + ) + })?; + Ok(ComputeResponse::zk( + ZkResponse::DkgAggregation(DkgAggregationResponse { proof }), + request.correlation_id, + request.e3_id, + )) +} + +fn handle_decryption_aggregation_proof( + prover: &ZkProver, + req: DecryptionAggregationRequest, + request: ComputeRequest, +) -> Result { + let artifacts_dir = req.params_preset.artifacts_dir(); + let job_id = zk_bb_work_id(&request); + let jobs: Vec = req + .jobs + .iter() + .map(|j| DecryptionAggregationJob { + c6_inner_proofs: &j.c6_inner_proofs, + c6_slot_indices: &j.c6_slot_indices, + c7_proof: &j.c7_proof, + }) + .collect(); + let proofs = prove_decryption_aggregation_jobs( + prover, + req.c6_total_slots, + &jobs, + &job_id, + &artifacts_dir, + ) + .map_err(|e| { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), + request.clone(), + ) + })?; + Ok(ComputeResponse::zk( + ZkResponse::DecryptionAggregation(DecryptionAggregationResponse { proofs }), + request.correlation_id, + request.e3_id, + )) +} + /// Helper to reduce boilerplate for ZK errors fn make_zk_error(request: &ComputeRequest, msg: String) -> ComputeRequestError { ComputeRequestError::new( @@ -803,7 +865,6 @@ fn handle_share_computation_proof( let bb_work = zk_bb_work_id(&request); let inner_job_id = format!("{bb_work}_c2_inner"); - let wrap_job_id = format!("{bb_work}_c2_wrap"); let artifacts_dir = req.params_preset.artifacts_dir(); // 7. Inner C2 proof (sk_share_computation or e_sm_share_computation) @@ -823,20 +884,9 @@ fn handle_share_computation_proof( ) })?; - // 8. Wrap inner proof for fold aggregation (`share_computation` wrapper) - let wrapped_proof = generate_wrapper_proof(prover, &proof, &wrap_job_id, &artifacts_dir) - .map_err(|e| { - ComputeRequestError::new( - ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), - request.clone(), - ) - })?; - - // 9. Return inner + wrapped C2 proofs Ok(ComputeResponse::zk( ZkResponse::ShareComputation(ShareComputationProofResponse { proof, - wrapped_proof, dkg_input_type: req.dkg_input_type, }), request.correlation_id, @@ -899,7 +949,6 @@ fn handle_pk_generation_proof( // 5. Generate proof via Provable trait let circuit = PkGenerationCircuit; let bb_work = zk_bb_work_id(&request); - let wrap_job_id = format!("{bb_work}_c1_wrap"); let artifacts_dir = req.params_preset.artifacts_dir(); let proof = circuit @@ -917,17 +966,8 @@ fn handle_pk_generation_proof( ) })?; - let wrapped_proof = generate_wrapper_proof(prover, &proof, &wrap_job_id, &artifacts_dir) - .map_err(|e| { - ComputeRequestError::new( - ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), - request.clone(), - ) - })?; - - // 6. Return response Ok(ComputeResponse::zk( - ZkResponse::PkGeneration(PkGenerationProofResponse::new(proof, wrapped_proof)), + ZkResponse::PkGeneration(PkGenerationProofResponse::new(proof)), request.correlation_id, request.e3_id, )) @@ -954,7 +994,6 @@ fn handle_pk_bfv_proof( let circuit = PkCircuit; let circuit_data = PkCircuitData { public_key: pk_bfv }; let bb_work = zk_bb_work_id(&request); - let wrap_job_id = format!("{bb_work}_c0_wrap"); let preset_counterpart = req .params_preset .threshold_counterpart() @@ -977,16 +1016,8 @@ fn handle_pk_bfv_proof( ) })?; - let wrapped_proof = generate_wrapper_proof(prover, &proof, &wrap_job_id, &artifacts_dir) - .map_err(|e| { - ComputeRequestError::new( - ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), - request.clone(), - ) - })?; - Ok(ComputeResponse::zk( - ZkResponse::PkBfv(PkBfvProofResponse::new(proof, wrapped_proof)), + ZkResponse::PkBfv(PkBfvProofResponse::new(proof)), request.correlation_id, request.e3_id, )) @@ -1056,7 +1087,6 @@ fn handle_share_encryption_proof( // 6. Generate proof (preset = threshold preset; Inputs::compute derives DKG internally) let circuit = ShareEncryptionCircuit; let bb_work = zk_bb_work_id(&request); - let wrap_job_id = format!("{bb_work}_c3_wrap"); let artifacts_dir = req.params_preset.artifacts_dir(); let proof = circuit .prove( @@ -1073,18 +1103,9 @@ fn handle_share_encryption_proof( ) })?; - let wrapped_proof = generate_wrapper_proof(prover, &proof, &wrap_job_id, &artifacts_dir) - .map_err(|e| { - ComputeRequestError::new( - ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), - request.clone(), - ) - })?; - Ok(ComputeResponse::zk( ZkResponse::ShareEncryption(ShareEncryptionProofResponse { proof, - wrapped_proof, dkg_input_type: req.dkg_input_type, recipient_party_id: req.recipient_party_id, row_index: req.row_index, @@ -1158,7 +1179,6 @@ fn handle_dkg_share_decryption_proof( // 5. Generate proof let circuit = ShareDecryptionCircuit; let bb_work = zk_bb_work_id(&request); - let wrap_job_id = format!("{bb_work}_c4_wrap"); let artifacts_dir = req.params_preset.artifacts_dir(); let proof = circuit .prove( @@ -1175,19 +1195,9 @@ fn handle_dkg_share_decryption_proof( ) })?; - let wrapped_proof = generate_wrapper_proof(prover, &proof, &wrap_job_id, &artifacts_dir) - .map_err(|e| { - ComputeRequestError::new( - ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), - request.clone(), - ) - })?; - - // 6. Return response Ok(ComputeResponse::zk( ZkResponse::DkgShareDecryption(DkgShareDecryptionProofResponse { proof, - wrapped_proof, dkg_input_type: req.dkg_input_type, }), request.correlation_id, @@ -1195,8 +1205,7 @@ fn handle_dkg_share_decryption_proof( )) } -/// ZK-verify a share proof: inner recursive circuits use `verify_proof`; the C2 wrapper -/// (`ShareComputation`) uses the wrapper VK path and Default bb target. +/// ZK-verify a share proof (inner recursive circuits). fn zk_verify_share_proof_bundle( prover: &ZkProver, proof: &Proof, @@ -1204,11 +1213,7 @@ fn zk_verify_share_proof_bundle( party_id: u64, artifacts_dir: &str, ) -> Result { - if proof.circuit == CircuitName::ShareComputation { - prover.verify_wrapper_proof(proof, e3_id_str, party_id, artifacts_dir) - } else { - prover.verify_proof(proof, e3_id_str, party_id, artifacts_dir) - } + prover.verify_proof(proof, e3_id_str, party_id, artifacts_dir) } fn handle_verify_share_proofs( @@ -1441,7 +1446,9 @@ fn handle_decrypted_shares_aggregation_proof( threshold: req.threshold_m as usize, }; - // e. Build circuit data and generate proof (C7 is always EVM-targeted for on-chain verification) + // e. C7 uses noir-recursive-no-zk (non-ZK recursive); it is verified inside + // `DecryptionAggregator` via `verify_honk_proof_non_zk`. The EVM-facing proof for on-chain + // is `CircuitName::DecryptionAggregator`. let circuit_data = DecryptedSharesAggregationCircuitData { committee, d_share_polys, @@ -1458,7 +1465,7 @@ fn handle_decrypted_shares_aggregation_proof( &req.params_preset, &circuit_data, &idx_work_id, - CircuitVariant::Evm, + CircuitVariant::Default, &artifacts_dir, ) .map_err(|e| { diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 21a64e1abc..465c94054e 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -85,19 +85,53 @@ async fn setup_test_zk_backend() -> Result<(ZkBackend, tempfile::TempDir)> { .join("bin"); let dkg_target = circuits_build_root.join("dkg").join("target"); let threshold_target = circuits_build_root.join("threshold").join("target"); - let wrapper_dkg_target = circuits_build_root + let c3_fold_target = circuits_build_root .join("recursive_aggregation") - .join("wrapper") - .join("dkg") + .join("c3_fold") .join("target"); - let wrapper_threshold_target = circuits_build_root + let c3_fold_kernel_target = circuits_build_root .join("recursive_aggregation") - .join("wrapper") - .join("threshold") + .join("c3_fold_kernel") .join("target"); - let fold_target = circuits_build_root + let c6_fold_target = circuits_build_root .join("recursive_aggregation") - .join("fold") + .join("c6_fold") + .join("target"); + let c6_fold_kernel_target = circuits_build_root + .join("recursive_aggregation") + .join("c6_fold_kernel") + .join("target"); + let c2ab_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c2ab_fold") + .join("target"); + let c3ab_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c3ab_fold") + .join("target"); + let c4ab_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c4ab_fold") + .join("target"); + let node_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("node_fold") + .join("target"); + let nodes_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("nodes_fold") + .join("target"); + let nodes_fold_kernel_target = circuits_build_root + .join("recursive_aggregation") + .join("nodes_fold_kernel") + .join("target"); + let dkg_aggregator_target = circuits_build_root + .join("recursive_aggregation") + .join("dkg_aggregator") + .join("target"); + let decryption_aggregator_target = circuits_build_root + .join("recursive_aggregation") + .join("decryption_aggregator") .join("target"); // Helper: copy {name}.json + VK artifacts into a destination directory. @@ -221,117 +255,155 @@ async fn setup_test_zk_backend() -> Result<(ZkBackend, tempfile::TempDir)> { ) .await; - // ── default/ variant (wrapper & fold proofs, uses .vk_recursive) ─── + // ── default/ variant (recursive aggregation bins, uses .vk_recursive) ─── let dv = preset_dir.join("default"); - // DKG wrapper circuits - let dkg_wrapper_base = dv.join("recursive_aggregation/wrapper/dkg"); + // C5 (pk_aggregation) — proven with noir-recursive-no-zk and folded into + // DkgAggregator, so it must be staged under default/ too. copy_circuit( - &wrapper_dkg_target, - &dkg_wrapper_base.join("pk"), - "pk", + &threshold_target, + &dv.join("threshold/pk_aggregation"), + "pk_aggregation", ".vk_recursive", ".vk_recursive_hash", ) .await; + copy_circuit( - &wrapper_dkg_target, - &dkg_wrapper_base.join("share_computation"), - "share_computation", + &c3_fold_target, + &dv.join("recursive_aggregation/c3_fold"), + "c3_fold", ".vk_recursive", ".vk_recursive_hash", ) .await; copy_circuit( - &wrapper_dkg_target, - &dkg_wrapper_base.join("share_encryption"), - "share_encryption", + &c3_fold_kernel_target, + &dv.join("recursive_aggregation/c3_fold_kernel"), + "c3_fold_kernel", ".vk_recursive", ".vk_recursive_hash", ) .await; copy_circuit( - &wrapper_dkg_target, - &dkg_wrapper_base.join("share_decryption"), - "share_decryption", + &c6_fold_target, + &dv.join("recursive_aggregation/c6_fold"), + "c6_fold", ".vk_recursive", ".vk_recursive_hash", ) .await; - - // Threshold wrapper circuits - let threshold_wrapper_base = dv.join("recursive_aggregation/wrapper/threshold"); copy_circuit( - &wrapper_threshold_target, - &threshold_wrapper_base.join("pk_generation"), - "pk_generation", + &c6_fold_kernel_target, + &dv.join("recursive_aggregation/c6_fold_kernel"), + "c6_fold_kernel", ".vk_recursive", ".vk_recursive_hash", ) .await; copy_circuit( - &wrapper_threshold_target, - &threshold_wrapper_base.join("pk_aggregation"), - "pk_aggregation", + &c2ab_fold_target, + &dv.join("recursive_aggregation/c2ab_fold"), + "c2ab_fold", ".vk_recursive", ".vk_recursive_hash", ) .await; copy_circuit( - &wrapper_threshold_target, - &threshold_wrapper_base.join("share_decryption"), - "share_decryption", + &c3ab_fold_target, + &dv.join("recursive_aggregation/c3ab_fold"), + "c3ab_fold", ".vk_recursive", ".vk_recursive_hash", ) .await; copy_circuit( - &wrapper_threshold_target, - &threshold_wrapper_base.join("decrypted_shares_aggregation"), - "decrypted_shares_aggregation", + &c4ab_fold_target, + &dv.join("recursive_aggregation/c4ab_fold"), + "c4ab_fold", ".vk_recursive", ".vk_recursive_hash", ) .await; - - // Fold circuit (default variant) copy_circuit( - &fold_target, - &dv.join("recursive_aggregation/fold"), - "fold", + &node_fold_target, + &dv.join("recursive_aggregation/node_fold"), + "node_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await; + copy_circuit( + &nodes_fold_target, + &dv.join("recursive_aggregation/nodes_fold"), + "nodes_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await; + copy_circuit( + &nodes_fold_kernel_target, + &dv.join("recursive_aggregation/nodes_fold_kernel"), + "nodes_fold_kernel", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await; + copy_circuit( + &dkg_aggregator_target, + &dv.join("recursive_aggregation/dkg_aggregator"), + "dkg_aggregator", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await; + copy_circuit( + &decryption_aggregator_target, + &dv.join("recursive_aggregation/decryption_aggregator"), + "decryption_aggregator", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await; + // C7 (decrypted_shares_aggregation) — proven with noir-recursive-no-zk and + // folded into DecryptionAggregator, so it must also be staged under default/. + copy_circuit( + &threshold_target, + &dv.join("threshold/decrypted_shares_aggregation"), + "decrypted_shares_aggregation", ".vk_recursive", ".vk_recursive_hash", ) .await; - // ── evm/ variant (on-chain verification: C5, C7, fold) ─────────── + // ── evm/ variant (on-chain verification: DKG aggregator, C7) ─────────── let ev = preset_dir.join("evm"); - // C5 (pk_aggregation) — EVM-targeted + // DKG aggregator — EVM-targeted (folds + C5 verified inside) copy_circuit( - &threshold_target, - &ev.join("threshold/pk_aggregation"), - "pk_aggregation", + &dkg_aggregator_target, + &ev.join("recursive_aggregation/dkg_aggregator"), + "dkg_aggregator", ".vk", ".vk_hash", ) .await; - // C7 (decrypted_shares_aggregation) — EVM-targeted + // Decryption aggregator — EVM-targeted (C6 fold + C7 verified inside) copy_circuit( - &threshold_target, - &ev.join("threshold/decrypted_shares_aggregation"), - "decrypted_shares_aggregation", + &decryption_aggregator_target, + &ev.join("recursive_aggregation/decryption_aggregator"), + "decryption_aggregator", ".vk", ".vk_hash", ) .await; - // Fold circuit — final EVM fold + // C7 (decrypted_shares_aggregation) — EVM-targeted copy_circuit( - &fold_target, - &ev.join("recursive_aggregation/fold"), - "fold", + &threshold_target, + &ev.join("threshold/decrypted_shares_aggregation"), + "decrypted_shares_aggregation", ".vk", ".vk_hash", ) @@ -593,7 +665,9 @@ fn plaintext_aggregator_marker(data: &EnclaveEventData, e3_id: &E3id) -> Option< ComputeRequestKind::Zk(ZkRequest::VerifyShareProofs(_)) | ComputeRequestKind::TrBFV(TrBFVRequest::CalculateThresholdDecryption(_)) | ComputeRequestKind::Zk(ZkRequest::DecryptedSharesAggregation(_)) - | ComputeRequestKind::Zk(ZkRequest::FoldProofs { .. }) + | ComputeRequestKind::Zk(ZkRequest::NodeDkgFold { .. }) + | ComputeRequestKind::Zk(ZkRequest::DkgAggregation { .. }) + | ComputeRequestKind::Zk(ZkRequest::DecryptionAggregation { .. }) ) => { Some("ComputeRequest") @@ -607,7 +681,9 @@ fn plaintext_aggregator_marker(data: &EnclaveEventData, e3_id: &E3id) -> Option< _ )) | ComputeResponseKind::Zk(ZkResponse::DecryptedSharesAggregation(_)) - | ComputeResponseKind::Zk(ZkResponse::FoldProofs(_)) + | ComputeResponseKind::Zk(ZkResponse::NodeDkgFold(_)) + | ComputeResponseKind::Zk(ZkResponse::DkgAggregation(_)) + | ComputeResponseKind::Zk(ZkResponse::DecryptionAggregation(_)) ) => { Some("ComputeResponse") @@ -880,7 +956,7 @@ async fn test_trbfv_actor() -> Result<()> { // Trigger actor DKG let e3_id = E3id::new("0", 1); - let proof_aggregation_enabled = false; + let proof_aggregation_enabled = true; let e3_requested = E3Requested { e3_id: e3_id.clone(), @@ -968,14 +1044,40 @@ async fn test_trbfv_actor() -> Result<()> { expected_events.extend_from_slice(&KS3); } expected_events.push("PublicKeyAggregated"); - let h = expect_node_events_with_timeouts( - &nodes, - 0, - &expected_events, - Duration::from_secs(5000), - Duration::from_secs(5000), - ) - .await?; + // KeyshareCreated and DKGRecursiveAggregationComplete are gossiped independently + // by different committee members, so at a non-committee observer they can interleave + // in any order. Assert the multiset matches and the boundary events are in place, + // rather than a strict sequence. + let h = nodes + .take_history_with_timeouts( + 0, + expected_events.len(), + Some(Duration::from_secs(5000)), + Some(Duration::from_secs(5000)), + ) + .await + .map_err(|e| anyhow::anyhow!("FAILURE on node 0: {expected_events:?} : {e}"))?; + let actual_types = h.event_types(); + println!("node 0 >> {:?} =~ {:?}", actual_types, expected_events); + let mut actual_sorted = actual_types.clone(); + let mut expected_sorted: Vec = + expected_events.iter().map(|s| (*s).to_string()).collect(); + actual_sorted.sort(); + expected_sorted.sort(); + assert_eq!( + actual_sorted, expected_sorted, + "node 0 event multiset mismatch" + ); + assert_eq!( + actual_types.first().map(String::as_str), + Some("AggregatorChanged"), + "node 0: first event must be AggregatorChanged" + ); + assert_eq!( + actual_types.last().map(String::as_str), + Some("PublicKeyAggregated"), + "node 0: last event must be PublicKeyAggregated" + ); let active_aggregator_history = nodes.get_history(active_aggregator_index).await?; let active_aggregator_pubkey_history_len = active_aggregator_history.len(); @@ -999,12 +1101,31 @@ async fn test_trbfv_actor() -> Result<()> { // The active aggregator is also a selected committee member, so its node history contains // local ThresholdKeyshare DKG work in addition to the public-key aggregation stage. Project // only the deterministic pubkey-aggregation signals instead of comparing the whole raw node bus. + // + // KeyshareCreated and DKGRecursiveAggregationComplete events are produced independently + // by each committee member and gossiped in parallel with the active aggregator's own + // C1→C5 verification flow, so their positions relative to the C1→C5 sub-sequence are + // non-deterministic. Compare as a multiset plus boundary events rather than strict order. let active_aggregator_pubkey_events = project_history(&active_aggregator_history, |data| { publickey_aggregator_marker(data, &e3_id) }); + let mut actual_sorted = active_aggregator_pubkey_events.clone(); + let mut expected_sorted = expected_active_aggregator_pubkey_events.clone(); + actual_sorted.sort(); + expected_sorted.sort(); + assert_eq!( + actual_sorted, expected_sorted, + "Active aggregator public-key flow: event multiset mismatch" + ); + assert_eq!( + active_aggregator_pubkey_events.first().copied(), + Some("CommitteeFinalized"), + "Active aggregator: first event must be CommitteeFinalized" + ); assert_eq!( - active_aggregator_pubkey_events, expected_active_aggregator_pubkey_events, - "Unexpected active aggregator public-key flow" + active_aggregator_pubkey_events.last().copied(), + Some("PublicKeyAggregated"), + "Active aggregator: last event must be PublicKeyAggregated" ); report.push(( @@ -1123,97 +1244,80 @@ async fn test_trbfv_actor() -> Result<()> { ); let completion_events = &active_aggregator_plaintext_events[C6_VERIFY_PREFIX.len()..]; - let c6_proof_count = threshold_n as usize * num_votes_per_voter; - let c6_fold_steps = if proof_aggregation_enabled { - c6_proof_count.saturating_sub(1) - } else { - 0 - }; + let mut expected_plaintext_completion = vec![ + "ComputeRequest", + "ComputeResponse", + "AggregationProofPending", + "ComputeRequest", + "ComputeResponse", + "AggregationProofSigned", + ]; if proof_aggregation_enabled { - let aggregation_pending_index = completion_events - .iter() - .position(|event| *event == "AggregationProofPending") - .expect("AggregationProofPending should be present"); - let aggregation_signed_index = completion_events - .iter() - .position(|event| *event == "AggregationProofSigned") - .expect("AggregationProofSigned should be present"); - let plaintext_aggregated_index = completion_events - .iter() - .position(|event| *event == "PlaintextAggregated") - .expect("PlaintextAggregated should be present"); - - assert_eq!( - completion_events.len(), - 7 + (2 * c6_fold_steps), - "Unexpected active aggregator C6/C7 completion event count" - ); - assert_eq!( - &completion_events[..2], - ["ComputeRequest", "ComputeRequest"] - ); - assert_eq!( - count_projected_events(completion_events, "ComputeRequest"), - 2 + c6_fold_steps - ); - assert_eq!( - count_projected_events(completion_events, "ComputeResponse"), - 2 + c6_fold_steps - ); - assert_eq!( - count_projected_events(completion_events, "AggregationProofPending"), - 1 - ); - assert_eq!( - count_projected_events(completion_events, "AggregationProofSigned"), - 1 - ); - assert_eq!( - count_projected_events(completion_events, "PlaintextAggregated"), - 1 - ); - assert!( - aggregation_pending_index < aggregation_signed_index, - "AggregationProofPending must precede AggregationProofSigned" - ); - assert_eq!( - plaintext_aggregated_index, - completion_events.len() - 1, - "PlaintextAggregated must be the last active aggregator completion event" - ); - } else { - assert_eq!( - completion_events, - [ - "ComputeRequest", - "ComputeResponse", - "AggregationProofPending", - "ComputeRequest", - "ComputeResponse", - "AggregationProofSigned", - "PlaintextAggregated", - ], - "Unexpected active aggregator plaintext completion flow" - ); + // `DecryptionAggregation`: one compute pair (C6 fold + C7 checked inside the worker). + expected_plaintext_completion.push("ComputeRequest"); + expected_plaintext_completion.push("ComputeResponse"); } + expected_plaintext_completion.push("PlaintextAggregated"); + + assert_eq!( + completion_events, + expected_plaintext_completion.as_slice(), + "Unexpected active aggregator plaintext completion flow" + ); + assert_eq!( + count_projected_events(completion_events, "AggregationProofPending"), + 1 + ); + assert_eq!( + count_projected_events(completion_events, "AggregationProofSigned"), + 1 + ); + assert_eq!( + count_projected_events(completion_events, "PlaintextAggregated"), + 1 + ); + let aggregation_pending_index = completion_events + .iter() + .position(|event| *event == "AggregationProofPending") + .expect("AggregationProofPending should be present"); + let aggregation_signed_index = completion_events + .iter() + .position(|event| *event == "AggregationProofSigned") + .expect("AggregationProofSigned should be present"); + let plaintext_aggregated_index = completion_events + .iter() + .position(|event| *event == "PlaintextAggregated") + .expect("PlaintextAggregated should be present"); + assert!( + aggregation_pending_index < aggregation_signed_index, + "AggregationProofPending must precede AggregationProofSigned" + ); + assert_eq!( + plaintext_aggregated_index, + completion_events.len() - 1, + "PlaintextAggregated must be the last active aggregator completion event" + ); report.push(( "Ciphertext published -> PlaintextAggregated", publishing_ct_timer.elapsed(), )); - let (plaintext, aggregation_proofs) = h + let (plaintext, decryption_aggregator_proofs) = h .iter() .rev() .find_map(|e| { if let EnclaveEventData::PlaintextAggregated(PlaintextAggregated { decrypted_output, - aggregation_proofs, + decryption_aggregator_proofs, .. }) = e.get_data() { - Some((decrypted_output.clone(), aggregation_proofs.clone())) + Some(( + decrypted_output.clone(), + decryption_aggregator_proofs.clone(), + )) } else { None } @@ -1226,8 +1330,8 @@ async fn test_trbfv_actor() -> Result<()> { })?; assert!( - !aggregation_proofs.is_empty(), - "C7 proofs should be present in PlaintextAggregated" + !decryption_aggregator_proofs.is_empty(), + "DecryptionAggregator proofs should be present in PlaintextAggregated" ); let results = plaintext @@ -1318,15 +1422,13 @@ async fn test_p2p_actor_forwards_events_to_network() -> Result<()> { let evt_1 = PlaintextAggregated { e3_id: E3id::new("1235", 1), decrypted_output: vec![ArcBytes::from_bytes(&[1, 2, 3, 4])], - aggregation_proofs: vec![], - c6_aggregated_proof: None, + decryption_aggregator_proofs: vec![], }; let evt_2 = PlaintextAggregated { e3_id: E3id::new("1236", 1), decrypted_output: vec![ArcBytes::from_bytes(&[1, 2, 3, 4])], - aggregation_proofs: vec![], - c6_aggregated_proof: None, + decryption_aggregator_proofs: vec![], }; let local_evt_3 = CiphernodeSelected { @@ -1775,14 +1877,18 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { .send(TakeEvents::::new(28)) .await?; + let actual_pk_commitment_1 = match history.events.last().cloned().unwrap().into_data() { + e3_events::EnclaveEventData::PublicKeyAggregated(ev) => ev.pk_commitment, + other => panic!("expected PublicKeyAggregated, got {other:?}"), + }; assert_eq!( history.events.last().cloned().unwrap().into_data(), PublicKeyAggregated { pubkey: ArcBytes::from_bytes(&test_pubkey.to_bytes()), e3_id: E3id::new("1234", 1), nodes: OrderedSet::from(eth_addrs.clone()), - pk_aggregation_proof: None, - dkg_aggregated_proof: None, + pk_commitment: actual_pk_commitment_1, + dkg_aggregator_proof: None, } .into() ); @@ -1812,14 +1918,18 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { .send(TakeEvents::::new(8)) .await?; + let actual_pk_commitment_2 = match history.events.last().cloned().unwrap().into_data() { + e3_events::EnclaveEventData::PublicKeyAggregated(ev) => ev.pk_commitment, + other => panic!("expected PublicKeyAggregated, got {other:?}"), + }; assert_eq!( history.events.last().cloned().unwrap().into_data(), PublicKeyAggregated { pubkey: ArcBytes::from_bytes(&test_pubkey.to_bytes()), e3_id: E3id::new("1234", 2), nodes: OrderedSet::from(eth_addrs.clone()), - pk_aggregation_proof: None, - dkg_aggregated_proof: None, + pk_commitment: actual_pk_commitment_2, + dkg_aggregator_proof: None, } .into() ); diff --git a/crates/zk-helpers/src/circuits/commitments.rs b/crates/zk-helpers/src/circuits/commitments.rs index dd31640acd..747371ec59 100644 --- a/crates/zk-helpers/src/circuits/commitments.rs +++ b/crates/zk-helpers/src/circuits/commitments.rs @@ -457,14 +457,16 @@ fn truncate_crt_polynomial_to_max_coeffs(crt: &CrtPolynomial, max_len: usize) -> /// Commitment to a threshold decryption share: all CRT limbs, first `max_k` coefficients /// per limb (matches Noir `compute_threshold_decryption_share_commitment`). +/// +/// `native_coeff_bit` must be the max bit width of coefficients in \([0, q_l)\) per CRT limb. pub fn compute_threshold_decryption_share_commitment( d_share: &CrtPolynomial, - bit_d: u32, + native_coeff_bit: u32, max_k: usize, ) -> BigInt { let truncated = truncate_crt_polynomial_to_max_coeffs(d_share, max_k); let mut payload = Vec::new(); - payload = flatten(payload, &truncated.limbs, bit_d); + payload = flatten(payload, &truncated.limbs, native_coeff_bit); let input_size = payload.len() as u32; let io_pattern = [0x80000000 | input_size, 1]; diff --git a/crates/zk-helpers/src/circuits/output_layout.rs b/crates/zk-helpers/src/circuits/output_layout.rs index 1ed80c815a..7131792cb5 100644 --- a/crates/zk-helpers/src/circuits/output_layout.rs +++ b/crates/zk-helpers/src/circuits/output_layout.rs @@ -111,8 +111,14 @@ impl CircuitOutputLayout { } /// C6 — Threshold share decryption public inputs. -pub const THRESHOLD_SHARE_DECRYPTION_INPUTS: &[OutputField] = - &[f("expected_sk_commitment"), f("expected_e_sm_commitment")]; +pub const THRESHOLD_SHARE_DECRYPTION_INPUTS: &[OutputField] = &[ + f("expected_sk_commitment"), + f("expected_e_sm_commitment"), + f("ct_commitment"), +]; + +/// C3 — Share encryption public return (`-> pub Field`). +pub const SHARE_ENCRYPTION_OUTPUTS: &[OutputField] = &[f("ct_commitment")]; // ── Per-circuit output field constants ────────────────────────────────────── @@ -313,11 +319,12 @@ mod tests { let layout = CircuitOutputLayout::Fixed { fields: THRESHOLD_SHARE_DECRYPTION_OUTPUTS, }; - // C6: 2 public inputs + 1 output = 96 bytes - let mut signals = vec![0u8; 96]; + // C6: 3 public inputs + 1 output = 128 bytes + let mut signals = vec![0u8; 128]; signals[0..32].copy_from_slice(&[0x11; 32]); signals[32..64].copy_from_slice(&[0x22; 32]); - signals[64..96].copy_from_slice(&[0x77; 32]); + signals[64..96].copy_from_slice(&[0x33; 32]); + signals[96..128].copy_from_slice(&[0x77; 32]); assert_eq!( layout.extract_field(&signals, "d_commitment").unwrap(), @@ -358,7 +365,7 @@ mod tests { let mut signals = vec![0u8; 96]; signals[0..32].copy_from_slice(&[0x11; 32]); signals[32..64].copy_from_slice(&[0x22; 32]); - signals[64..96].copy_from_slice(&[0x77; 32]); + signals[64..96].copy_from_slice(&[0x33; 32]); assert_eq!( layout @@ -372,6 +379,10 @@ mod tests { .unwrap(), &[0x22; 32] ); + assert_eq!( + layout.extract_field(&signals, "ct_commitment").unwrap(), + &[0x33; 32] + ); } #[test] @@ -379,9 +390,7 @@ mod tests { let layout = CircuitInputLayout::Fixed { fields: THRESHOLD_SHARE_DECRYPTION_INPUTS, }; - assert!(layout - .extract_field(&[0u8; 32], "expected_e_sm_commitment") - .is_none()); + assert!(layout.extract_field(&[0u8; 64], "ct_commitment").is_none()); } #[test] diff --git a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/codegen.rs b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/codegen.rs index b689d9862d..540afcfece 100644 --- a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/codegen.rs +++ b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/codegen.rs @@ -72,7 +72,7 @@ decrypted_shares_aggregation (CIRCUIT 7) ************************************/ pub global {}_BIT_NOISE: u32 = {}; -pub global {}_BIT_D: u32 = {}; +pub global {}_BIT_D_NATIVE: u32 = {}; pub global {}_CONFIGS: DecryptedSharesAggregationConfigs = DecryptedSharesAggregationConfigs::new(QIS, PLAINTEXT_MODULUS, Q_INVERSE_MOD_T); @@ -86,7 +86,7 @@ pub global {}_CONFIGS: DecryptedSharesAggregationConfigs = prefix, configs.bits.noise_bit, prefix, - configs.bits.d_bit, + configs.bits.d_native_bit, prefix, ) } @@ -109,9 +109,10 @@ mod tests { "{}_BIT_NOISE: u32 = {}", prefix, configs.bits.noise_bit ))); - assert!( - codegen_configs.contains(&format!("{}_BIT_D: u32 = {}", prefix, configs.bits.d_bit)) - ); + assert!(codegen_configs.contains(&format!( + "{}_BIT_D_NATIVE: u32 = {}", + prefix, configs.bits.d_native_bit + ))); assert!(codegen_configs.contains(&format!("{}_CONFIGS:", prefix))); assert!(codegen_configs.contains( "DecryptedSharesAggregationConfigs::new(QIS, PLAINTEXT_MODULUS, Q_INVERSE_MOD_T)" @@ -134,7 +135,7 @@ mod tests { .contains("DECRYPTED_SHARES_AGGREGATION_BIT_NOISE")); assert!(artifacts .configs - .contains("DECRYPTED_SHARES_AGGREGATION_BIT_D")); + .contains("DECRYPTED_SHARES_AGGREGATION_BIT_D_NATIVE")); assert!(artifacts .configs .contains("DECRYPTED_SHARES_AGGREGATION_CONFIGS")); diff --git a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs index eb33a6bf7e..c07dc9f525 100644 --- a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs @@ -70,8 +70,8 @@ pub struct Bounds { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Bits { pub noise_bit: u32, - /// Coefficient bit width for decryption-share commitments (aligned with threshold share_decryption BIT_D). - pub d_bit: u32, + /// Native \([0, q_l)\) width for hashing decryption-share coefficients (C6/C7 `d_commitment`). + pub d_native_bit: u32, } /// Circuit config: moduli count, plaintext modulus, q_inverse_mod_t, bits, bounds, and message polynomial length. @@ -138,12 +138,15 @@ impl Computation for Bits { let ctx = threshold_params .ctx_at_level(0) .map_err(|e| CircuitsErrors::Other(format!("ctx_at_level: {:?}", e)))?; - let mut d_bit = 0u32; + let mut d_native_bit = 0u32; for qi in ctx.moduli_operators() { - let qi_bound = (BigInt::from(qi.modulus()) - 1) / 2; - d_bit = d_bit.max(calculate_bit_width(qi_bound)); + let q = BigInt::from(qi.modulus()); + d_native_bit = d_native_bit.max(calculate_bit_width(q - 1)); } - Ok(Bits { noise_bit, d_bit }) + Ok(Bits { + noise_bit, + d_native_bit, + }) } } @@ -330,7 +333,7 @@ impl Computation for Inputs { .map(|share| { compute_threshold_decryption_share_commitment( share, - configs.bits.d_bit, + configs.bits.d_native_bit, max_msg_non_zero_coeffs, ) }) @@ -391,7 +394,7 @@ mod tests { assert!(!bounds.delta_half.is_zero()); assert!(bounds.delta_half < bounds.delta); assert!(bits.noise_bit > 0); - assert!(bits.d_bit > 0); + assert!(bits.d_native_bit > 0); } #[test] @@ -434,6 +437,6 @@ mod tests { configs.max_msg_non_zero_coeffs ); assert!(out.bits.noise_bit > 0); - assert!(out.bits.d_bit > 0); + assert!(out.bits.d_native_bit > 0); } } diff --git a/crates/zk-helpers/src/circuits/threshold/share_decryption/codegen.rs b/crates/zk-helpers/src/circuits/threshold/share_decryption/codegen.rs index c51aa16b11..fdf3ad3c4c 100644 --- a/crates/zk-helpers/src/circuits/threshold/share_decryption/codegen.rs +++ b/crates/zk-helpers/src/circuits/threshold/share_decryption/codegen.rs @@ -69,6 +69,7 @@ pub global {}_BIT_E_SM: u32 = {}; pub global {}_BIT_R1: u32 = {}; pub global {}_BIT_R2: u32 = {}; pub global {}_BIT_D: u32 = {}; +pub global {}_BIT_D_NATIVE: u32 = {}; pub global {}_R1_BOUNDS: [Field; L] = [{}]; pub global {}_R2_BOUNDS: [Field; L] = [{}]; @@ -95,6 +96,8 @@ pub global {}_CONFIGS: ShareDecryptionConfigs = ShareDecryptionConfigs::new( prefix, configs.bits.d_bit, prefix, + configs.bits.d_native_bit, + prefix, r1_bounds_str, prefix, r2_bounds_str, diff --git a/crates/zk-helpers/src/circuits/threshold/share_decryption/computation.rs b/crates/zk-helpers/src/circuits/threshold/share_decryption/computation.rs index 644351809f..aa5630e402 100644 --- a/crates/zk-helpers/src/circuits/threshold/share_decryption/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/share_decryption/computation.rs @@ -11,8 +11,12 @@ //! [`Computation`] and are used by codegen. use crate::calculate_bit_width; -use crate::circuits::commitments::compute_aggregated_shares_commitment; +use crate::circuits::commitments::{ + compute_aggregated_shares_commitment, compute_ciphertext_commitment, +}; +use crate::circuits::threshold::decrypted_shares_aggregation::MAX_MSG_NON_ZERO_COEFFS; use crate::compute_modulus_bit; +use crate::compute_native_crt_coeff_bit; use crate::crt_polynomial_to_toml_json; use crate::decompose_residue; use crate::threshold::share_decryption::circuit::ShareDecryptionCircuit; @@ -26,11 +30,38 @@ use e3_polynomial::Polynomial; use itertools::izip; use num_bigint::BigInt; use num_bigint::BigUint; -use num_traits::ToPrimitive; +use num_traits::{ToPrimitive, Zero}; use rayon::iter::ParallelBridge; use rayon::iter::ParallelIterator; use serde::{Deserialize, Serialize}; +/// Low-degree native \([0, q)\) CRT limbs for `d_commitment`, matching C7's `from_fhe` truncation. +/// In each limb, `d` is reversed+centered (witness layout); native coeff `j` is `uncenter(d[N-1-j])`. +fn d_native_trunc_from_centered_d( + d: &CrtPolynomial, + moduli: &[u64], + degree: usize, + max_k: usize, +) -> CrtPolynomial { + let mut limbs = Vec::with_capacity(d.limbs.len()); + for (limb_idx, limb) in d.limbs.iter().enumerate() { + let q = BigInt::from(moduli[limb_idx]); + let coeffs = limb.coefficients(); + let mut asc = Vec::with_capacity(max_k); + for j in 0..max_k { + let w = &coeffs[degree - 1 - j]; + let u = if w < &BigInt::zero() { + w + &q + } else { + w.clone() + }; + asc.push(u); + } + limbs.push(Polynomial::new(asc)); + } + CrtPolynomial::new(limbs) +} + /// Output of [`CircuitComputation::compute`] for [`ShareDecryptionCircuit`]: bounds, bit widths, and inputs. #[derive(Debug)] pub struct ShareDecryptionComputationOutput { @@ -75,7 +106,10 @@ pub struct Bits { pub e_sm_bit: u32, pub r1_bit: u32, pub r2_bit: u32, + /// Centered `d` coefficient bound (payload flatten / Fiat–Shamir). pub d_bit: u32, + /// Native \([0, q)\) limb width for `d_native_trunc` / C7 share commitments. + pub d_native_bit: u32, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -93,8 +127,11 @@ pub struct Inputs { pub r1: CrtPolynomial, pub r2: CrtPolynomial, pub d: CrtPolynomial, + /// Native truncated `d` per limb (C7-compatible); hashed for public `d_commitment`. + pub d_native_trunc: CrtPolynomial, pub expected_sk_commitment: BigInt, pub expected_e_sm_commitment: BigInt, + pub ct_commitment: BigInt, } impl Computation for Configs { @@ -126,7 +163,7 @@ impl Computation for Bits { type Data = Bounds; type Error = CircuitsErrors; - fn compute(_: Self::Preset, data: &Self::Data) -> Result { + fn compute(preset: Self::Preset, data: &Self::Data) -> Result { // For r1, use the maximum of all low and up bounds let mut r1_bit = 0; for bound in data.r1_bounds.iter() { @@ -139,6 +176,10 @@ impl Computation for Bits { r2_bit = r2_bit.max(calculate_bit_width(BigInt::from(bound.clone()))); } + let (threshold_params, _) = + build_pair_for_preset(preset).map_err(|e| CircuitsErrors::Other(e.to_string()))?; + let d_native_bit = compute_native_crt_coeff_bit(threshold_params.moduli()); + Ok(Bits { ct_bit: r2_bit, sk_bit: r2_bit, @@ -146,6 +187,7 @@ impl Computation for Bits { r1_bit, r2_bit, d_bit: r2_bit, + d_native_bit, }) } } @@ -306,6 +348,14 @@ impl Computation for Inputs { let expected_sk_commitment = compute_aggregated_shares_commitment(&sk, modulus_bit); let expected_e_sm_commitment = compute_aggregated_shares_commitment(&e_sm, modulus_bit); + let bounds = Bounds::compute(preset, &())?; + let bits = Bits::compute(preset, &bounds)?; + let ct_commitment = compute_ciphertext_commitment(&ct0, &ct1, bits.ct_bit); + + let moduli_u64: Vec = threshold_params.moduli().to_vec(); + let d_native_trunc = + d_native_trunc_from_centered_d(&d, &moduli_u64, n as usize, MAX_MSG_NON_ZERO_COEFFS); + Ok(Inputs { ct0, ct1, @@ -314,8 +364,10 @@ impl Computation for Inputs { r1, r2, d, + d_native_trunc, expected_sk_commitment, expected_e_sm_commitment, + ct_commitment, }) } @@ -327,8 +379,10 @@ impl Computation for Inputs { let r1 = crt_polynomial_to_toml_json(&self.r1); let r2 = crt_polynomial_to_toml_json(&self.r2); let d = crt_polynomial_to_toml_json(&self.d); + let d_native_trunc = crt_polynomial_to_toml_json(&self.d_native_trunc); let expected_sk_commitment = self.expected_sk_commitment.to_string(); let expected_e_sm_commitment = self.expected_e_sm_commitment.to_string(); + let ct_commitment = self.ct_commitment.to_string(); let json = serde_json::json!({ "ct0": ct0, @@ -338,8 +392,10 @@ impl Computation for Inputs { "r1": r1, "r2": r2, "d": d, + "d_native_trunc": d_native_trunc, "expected_sk_commitment": expected_sk_commitment, "expected_e_sm_commitment": expected_e_sm_commitment, + "ct_commitment": ct_commitment, }); Ok(json) @@ -363,13 +419,22 @@ mod tests { assert_eq!(bits.d_bit, expected_bit); } - /// Verifies that `CrtPolynomial::reverse()` + `center()` matches - /// `Inputs::compute` for d_commitment, and that the Poly bytes round-trip - /// is lossless. + #[test] + fn test_d_native_bit_matches_moduli_and_covers_centered_d_bit() { + let (threshold_params, _) = build_pair_for_preset(DEFAULT_BFV_PRESET).unwrap(); + let bounds = Bounds::compute(DEFAULT_BFV_PRESET, &()).unwrap(); + let bits = Bits::compute(DEFAULT_BFV_PRESET, &bounds).unwrap(); + assert_eq!( + bits.d_native_bit, + crate::compute_native_crt_coeff_bit(threshold_params.moduli()) + ); + assert!(bits.d_native_bit >= bits.d_bit); + } + + /// `d_commitment` matches C7: hash of native truncated `from_fhe` limbs, via `d_native_trunc`. #[test] fn test_d_commitment_matches_inputs_compute() { use crate::circuits::commitments::compute_threshold_decryption_share_commitment; - use crate::circuits::threshold::decrypted_shares_aggregation::MAX_MSG_NON_ZERO_COEFFS; use crate::threshold::share_decryption::ShareDecryptionCircuitData; use crate::CiphernodesCommitteeSize; use fhe_math::rq::{Poly, Representation}; @@ -382,29 +447,19 @@ mod tests { let (threshold_params, _) = build_pair_for_preset(preset).unwrap(); let bounds = Bounds::compute(preset, &()).unwrap(); let bits = Bits::compute(preset, &bounds).unwrap(); - let moduli: Vec = threshold_params.moduli().to_vec(); - // Ground truth: Inputs::compute (what the Noir prover receives) let inputs = Inputs::compute(preset, &sample).unwrap(); - let truth = compute_threshold_decryption_share_commitment( - &inputs.d, - bits.d_bit, + let from_d_native = compute_threshold_decryption_share_commitment( + &inputs.d_native_trunc, + bits.d_native_bit, MAX_MSG_NON_ZERO_COEFFS, ); - - // Aggregator path: CrtPolynomial::reverse() + center() - let mut crt = sample.d_share.clone(); - crt.reverse(); - crt.center(&moduli).unwrap(); - let from_api = compute_threshold_decryption_share_commitment( - &crt, - bits.d_bit, + let from_raw_share = compute_threshold_decryption_share_commitment( + &sample.d_share, + bits.d_native_bit, MAX_MSG_NON_ZERO_COEFFS, ); - assert_eq!( - truth, from_api, - "CrtPolynomial API must match Inputs::compute" - ); + assert_eq!(from_d_native, from_raw_share); // Bytes round-trip: Poly → to_bytes → from_bytes → from_fhe_polynomial let raw: Vec> = sample @@ -429,18 +484,13 @@ mod tests { let mut poly = Poly::zero(&ctx, Representation::PowerBasis); poly.set_coefficients(arr); let poly_rt = Poly::from_bytes(&poly.to_bytes(), &ctx).unwrap(); - let mut crt_rt = CrtPolynomial::from_fhe_polynomial(&poly_rt); - crt_rt.reverse(); - crt_rt.center(&moduli).unwrap(); + let crt_rt = CrtPolynomial::from_fhe_polynomial(&poly_rt); let from_bytes = compute_threshold_decryption_share_commitment( &crt_rt, - bits.d_bit, + bits.d_native_bit, MAX_MSG_NON_ZERO_COEFFS, ); - assert_eq!( - truth, from_bytes, - "Bytes round-trip must match Inputs::compute" - ); + assert_eq!(from_d_native, from_bytes); } #[test] diff --git a/crates/zk-helpers/src/utils.rs b/crates/zk-helpers/src/utils.rs index ed252a5326..da46e794a9 100644 --- a/crates/zk-helpers/src/utils.rs +++ b/crates/zk-helpers/src/utils.rs @@ -179,6 +179,17 @@ pub fn compute_max_modulus(moduli: &[u64]) -> u64 { moduli.iter().copied().max().unwrap() } +/// Bit width to pack/hash native CRT coefficients in \([0, q)\) (per-limb), for commitment I/O. +/// +/// Uses `max_l bits(q_l - 1)`, strictly wider than centered bounds `(q_l-1)/2` when needed. +pub fn compute_native_crt_coeff_bit(moduli: &[u64]) -> u32 { + moduli + .iter() + .map(|&q| calculate_bit_width(BigInt::from(q) - 1)) + .max() + .unwrap_or(1) +} + /// Computes the bit width of the message. /// /// # Arguments diff --git a/crates/zk-prover/Cargo.toml b/crates/zk-prover/Cargo.toml index f78f687240..0f8e9b9130 100644 --- a/crates/zk-prover/Cargo.toml +++ b/crates/zk-prover/Cargo.toml @@ -50,8 +50,11 @@ walkdir = "2.5" e3-test-helpers = { workspace = true } ark-bn254 = { workspace = true } ark-ff = { workspace = true } +fhe-traits = { workspace = true } +ndarray = { workspace = true } num-traits = { workspace = true } paste = "1" +rand = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } [features] diff --git a/crates/zk-prover/src/actors/commitment_links/c4a_to_c6.rs b/crates/zk-prover/src/actors/commitment_links/c4a_to_c6.rs index 3b64bcdbf3..31617d65a9 100644 --- a/crates/zk-prover/src/actors/commitment_links/c4a_to_c6.rs +++ b/crates/zk-prover/src/actors/commitment_links/c4a_to_c6.rs @@ -79,10 +79,11 @@ mod tests { let sk_commitment = make_field(42); let source_values = vec![sk_commitment]; - // C6 inputs: [expected_sk_commitment=42, expected_e_sm_commitment=99] + // C6 inputs: [sk, e_sm, ct_commitment] + optional tail (e.g. d_commitment) let mut c6_signals = Vec::new(); c6_signals.extend_from_slice(&sk_commitment); c6_signals.extend_from_slice(&make_field(99)); + c6_signals.extend_from_slice(&make_field(1)); assert!(link.check_signals(&source_values, &c6_signals)); } @@ -92,10 +93,10 @@ mod tests { let link = C4aToC6SkCommitmentLink; let source_values = vec![make_field(42)]; - // C6 inputs: [expected_sk_commitment=99, expected_e_sm_commitment=99] let mut c6_signals = Vec::new(); c6_signals.extend_from_slice(&make_field(99)); c6_signals.extend_from_slice(&make_field(99)); + c6_signals.extend_from_slice(&make_field(1)); assert!(!link.check_signals(&source_values, &c6_signals)); } @@ -104,7 +105,7 @@ mod tests { fn short_signals() { let link = C4aToC6SkCommitmentLink; assert!(link.extract_source_values(&[0u8; 10]).is_empty()); - // Empty source values means malformed proof — should be inconsistent - assert!(!link.check_signals(&[], &[0u8; 64])); + // 3 * FIELD_BYTE_LEN = C6 input layout (sk, e_sm, ct_commitment). + assert!(!link.check_signals(&[], &[0u8; 3 * FIELD_BYTE_LEN])); } } diff --git a/crates/zk-prover/src/actors/commitment_links/c4b_to_c6.rs b/crates/zk-prover/src/actors/commitment_links/c4b_to_c6.rs index e0f08cd658..05286ce0a3 100644 --- a/crates/zk-prover/src/actors/commitment_links/c4b_to_c6.rs +++ b/crates/zk-prover/src/actors/commitment_links/c4b_to_c6.rs @@ -78,10 +78,10 @@ mod tests { let esm_commitment = make_field(99); let source_values = vec![esm_commitment]; - // C6 inputs: [expected_sk_commitment=42, expected_e_sm_commitment=99] let mut c6_signals = Vec::new(); c6_signals.extend_from_slice(&make_field(42)); c6_signals.extend_from_slice(&esm_commitment); + c6_signals.extend_from_slice(&make_field(1)); assert!(link.check_signals(&source_values, &c6_signals)); } @@ -91,10 +91,10 @@ mod tests { let link = C4bToC6ESmCommitmentLink; let source_values = vec![make_field(99)]; - // C6 inputs: [expected_sk_commitment=42, expected_e_sm_commitment=42] let mut c6_signals = Vec::new(); c6_signals.extend_from_slice(&make_field(42)); c6_signals.extend_from_slice(&make_field(42)); + c6_signals.extend_from_slice(&make_field(1)); assert!(!link.check_signals(&source_values, &c6_signals)); } @@ -104,6 +104,6 @@ mod tests { let link = C4bToC6ESmCommitmentLink; assert!(link.extract_source_values(&[0u8; 10]).is_empty()); // Empty source values means malformed proof — should be inconsistent - assert!(!link.check_signals(&[], &[0u8; 64])); + assert!(!link.check_signals(&[], &[0u8; 3 * FIELD_BYTE_LEN])); } } diff --git a/crates/zk-prover/src/actors/node_proof_aggregator.rs b/crates/zk-prover/src/actors/node_proof_aggregator.rs index abfa05167f..e4d38e4445 100644 --- a/crates/zk-prover/src/actors/node_proof_aggregator.rs +++ b/crates/zk-prover/src/actors/node_proof_aggregator.rs @@ -4,20 +4,8 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -//! Incremental node-level proof aggregation for DKG. -//! -//! `NodeProofAggregator` subscribes to `ThresholdSharePending` to learn how -//! many proofs to expect per E3, then folds each `DKGInnerProofReady` wrapped -//! proof into a running aggregate in strict `seq` order, using a reorder -//! buffer for out-of-order arrivals. -//! -//! When all expected proofs have been folded, it publishes -//! `DKGRecursiveAggregationComplete`. -//! -//! Ordering guarantee: `ThresholdSharePending` is always published to the bus -//! before any `DKGInnerProofReady` for the same E3 (ProofRequestActor holds -//! back C0's event until after `ThresholdSharePending` is processed). -//! Arriving out of that order is treated as a programming error and logged. +//! Node-level DKG proof aggregation: buffer all inner proofs (C0–C4), then run one +//! [`ZkRequest::NodeDkgFold`] when [`ThresholdSharePending`] says the full set is ready. use std::collections::{BTreeMap, HashMap}; @@ -25,37 +13,38 @@ use actix::{Actor, Addr, Context, Handler}; use e3_events::{ BusHandle, ComputeRequest, ComputeRequestError, ComputeResponse, ComputeResponseKind, CorrelationId, DKGInnerProofReady, DKGRecursiveAggregationComplete, E3id, EnclaveEvent, - EnclaveEventData, EventContext, EventPublisher, EventSubscriber, EventType, Proof, Sequenced, - ThresholdSharePending, TypedEvent, ZkRequest, ZkResponse, + EnclaveEventData, EventContext, EventPublisher, EventSubscriber, EventType, NodeDkgFoldRequest, + Proof, Sequenced, ShareEncryptionProofRequest, ThresholdSharePending, TypedEvent, ZkRequest, + ZkResponse, }; +use e3_fhe_params::build_pair_for_preset; use tracing::{error, info, warn}; -/// Per-E3 rolling aggregation state for one node's proofs. -struct RollingAggregationState { +/// Metadata from [`ThresholdSharePending`] for slot indices and sizing. +struct NodeDkgFoldMeta { party_id: u64, - /// Total proofs expected (for progress logging). total_expected: usize, - /// Proofs buffered out-of-order, keyed by seq index. + sk_enc_count: usize, + e_sm_enc_count: usize, + sk_share_encryption_requests: Vec, + e_sm_share_encryption_requests: Vec, + committee_n: usize, + n_moduli: usize, + params_preset: e3_fhe_params::BfvPreset, +} + +/// Per-E3 collection state: buffer proofs by `seq` until the monolithic fold can run. +struct DkgProofCollectionState { + meta: NodeDkgFoldMeta, buffer: BTreeMap, - /// The running accumulated (folded) proof. - accumulated: Option, - /// Next seq index expected for folding. - next_to_aggregate: usize, - /// Number of proofs remaining to process (decrements on first store + each fold completion). - remaining: usize, - /// Correlation ID for the in-flight fold, if any. fold_correlation: Option, - /// EventContext for publishing. last_ec: EventContext, - /// BFV preset for circuit artifact resolution. - params_preset: e3_fhe_params::BfvPreset, } -/// Actor that incrementally folds DKG inner proofs into a single node-level proof. +/// Actor that collects DKG inner proofs and dispatches a single [`ZkRequest::NodeDkgFold`]. pub struct NodeProofAggregator { bus: BusHandle, - states: HashMap, - /// Reverse map: fold correlation_id -> e3_id. + states: HashMap, fold_correlation: HashMap, } @@ -81,8 +70,6 @@ impl NodeProofAggregator { let (msg, ec) = msg.into_components(); let e3_id = msg.e3_id.clone(); - // When proof aggregation is disabled, skip wrapping/folding entirely - // and immediately signal completion with no aggregated proof. if !msg.proof_aggregation_enabled { info!( "NodeProofAggregator: proof aggregation disabled for E3 {} — skipping", @@ -106,29 +93,48 @@ impl NodeProofAggregator { let sk_enc_count = msg.sk_share_encryption_requests.len(); let e_sm_enc_count = msg.e_sm_share_encryption_requests.len(); - // Must mirror the formula in ProofRequestActor::handle_threshold_share_pending: - // C0 + C1 + C2a + C2b + C3a×sk_enc + C3b×e_sm_enc + C4a + C4b let total_expected = 4 + sk_enc_count + e_sm_enc_count + 2; - let params_preset = msg.proof_request.params_preset; - self.states.entry(e3_id.clone()).or_insert_with(|| { - info!( - "NodeProofAggregator: initializing state for E3 {} party {} (total_expected={}, ~{} fold steps)", - e3_id, msg.full_share.party_id, total_expected, - total_expected.saturating_sub(1), - ); - RollingAggregationState { - party_id: msg.full_share.party_id, - total_expected, + let (committee_n, n_moduli) = match build_pair_for_preset(msg.proof_request.params_preset) { + Ok((threshold_params, _)) => { + let n = msg.proof_request.committee_size.values().n as usize; + (n, threshold_params.moduli().len()) + } + Err(e) => { + error!( + "NodeProofAggregator: build_pair_for_preset failed for E3 {}: {e}", + e3_id + ); + return; + } + }; + + let meta = NodeDkgFoldMeta { + party_id: msg.full_share.party_id, + total_expected, + sk_enc_count, + e_sm_enc_count, + sk_share_encryption_requests: msg.sk_share_encryption_requests.clone(), + e_sm_share_encryption_requests: msg.e_sm_share_encryption_requests.clone(), + committee_n, + n_moduli, + params_preset: msg.proof_request.params_preset, + }; + + info!( + "NodeProofAggregator: E3 {} party {} — expecting {} inner proofs (C0..C4) for NodeDkgFold", + e3_id, meta.party_id, total_expected, + ); + + self.states.insert( + e3_id.clone(), + DkgProofCollectionState { + meta, buffer: BTreeMap::new(), - accumulated: None, - next_to_aggregate: 0, - remaining: total_expected, fold_correlation: None, last_ec: ec, - params_preset, - } - }); + }, + ); } fn handle_inner_proof_ready(&mut self, msg: TypedEvent) { @@ -143,159 +149,143 @@ impl NodeProofAggregator { return; }; - state.buffer.insert(msg.seq, msg.wrapped_proof); + if state.fold_correlation.is_some() { + warn!( + "NodeProofAggregator: seq={} arrived while NodeDkgFold in flight — dropped", + msg.seq + ); + return; + } + + state.buffer.insert(msg.seq, msg.proof); state.last_ec = ec; - let buffered = state.buffer.len(); - let folded = state - .total_expected - .saturating_sub(state.remaining.saturating_add(buffered)); info!( - "NodeProofAggregator: buffered seq={} for E3 {} (buffered={}, folded={}, remaining={})", - msg.seq, e3_id, buffered, folded, state.remaining + "NodeProofAggregator: buffered seq={} for E3 {} (have {}/{})", + msg.seq, + e3_id, + state.buffer.len(), + state.meta.total_expected ); - self.try_advance(&e3_id); + self.try_dispatch_node_dkg_fold(&e3_id); } - fn try_advance(&mut self, e3_id: &E3id) { - loop { - let state = match self.states.get_mut(e3_id) { - Some(s) => s, - None => return, - }; - - if state.fold_correlation.is_some() || state.remaining == 0 { - return; - } - - let next_proof = match state.buffer.remove(&state.next_to_aggregate) { - Some(p) => p, - None => return, // not yet available - }; - - if state.accumulated.is_none() { - // First proof: store as accumulated - info!( - "NodeProofAggregator: storing first proof (seq={}) for E3 {}", - state.next_to_aggregate, e3_id - ); - state.accumulated = Some(next_proof); - state.remaining -= 1; - state.next_to_aggregate += 1; - - if state.remaining == 0 { - // Only one proof total — done immediately - self.publish_complete(e3_id); - return; - } - } else { - // Fold accumulated + next_proof - let acc = state.accumulated.take().expect("checked above"); - let acc_restore = acc.clone(); - let next_proof_restore = next_proof.clone(); - let seq = state.next_to_aggregate; - let corr = CorrelationId::new(); - let ec = state.last_ec.clone(); - let e3_id_clone = e3_id.clone(); - - let folds_completed = state.total_expected - state.remaining - 1; - let total_folds = state.total_expected.saturating_sub(1); - info!( - "NodeProofAggregator: dispatching fold step {}/{} (seq={}) for E3 {}", - folds_completed + 1, - total_folds, - seq, - e3_id - ); - - match self.bus.publish( - ComputeRequest::zk( - ZkRequest::FoldProofs { - proof1: acc, - proof2: next_proof, - target_evm: false, - params_preset: state.params_preset, - }, - corr, - e3_id_clone, - ), - ec, - ) { - Ok(()) => { - state.fold_correlation = Some(corr); - state.next_to_aggregate += 1; - self.fold_correlation.insert(corr, e3_id.clone()); - } - Err(err) => { - error!( - "NodeProofAggregator: failed to publish fold request for E3 {}: {err}", - e3_id - ); - state.accumulated = Some(acc_restore); - state.buffer.insert(seq, next_proof_restore); - } - } - - return; // wait for fold response - } + fn try_dispatch_node_dkg_fold(&mut self, e3_id: &E3id) { + let state = match self.states.get_mut(e3_id) { + Some(s) => s, + None => return, + }; + let n = state.meta.total_expected; + if state.buffer.len() != n || !(0..n).all(|i| state.buffer.contains_key(&i)) { + return; } - } - fn handle_fold_response(&mut self, correlation_id: &CorrelationId, proof: Proof) { - let Some(e3_id) = self.fold_correlation.remove(correlation_id) else { - return; + let meta = &state.meta; + let c3_total_slots = meta.committee_n * meta.n_moduli; + let slots_a: Vec = meta + .sk_share_encryption_requests + .iter() + .map(|r| r.c3_slot_index(meta.n_moduli)) + .collect(); + let slots_b: Vec = meta + .e_sm_share_encryption_requests + .iter() + .map(|r| r.c3_slot_index(meta.n_moduli)) + .collect(); + + let sk = meta.sk_enc_count; + let esm = meta.e_sm_enc_count; + let buf = &state.buffer; + let get = |seq: usize| { + buf.get(&seq) + .cloned() + .expect("buffer contains all seq indices") }; - let Some(state) = self.states.get_mut(&e3_id) else { - error!( - "NodeProofAggregator: received fold response for unknown E3 {}", - e3_id - ); - return; + let c0_proof = get(0); + let c1_proof = get(1); + let c2a_proof = get(2); + let c2b_proof = get(3); + let mut c3a_inner_proofs = Vec::with_capacity(sk); + for s in 0..sk { + c3a_inner_proofs.push(get(4 + s)); + } + let mut c3b_inner_proofs = Vec::with_capacity(esm); + for s in 0..esm { + c3b_inner_proofs.push(get(4 + sk + s)); + } + let c4a_seq = 4 + sk + esm; + let c4a_proof = get(c4a_seq); + let c4b_proof = get(c4a_seq + 1); + + let corr = CorrelationId::new(); + let ec = state.last_ec.clone(); + let party_id = meta.party_id; + let preset = meta.params_preset; + + let req = NodeDkgFoldRequest { + c0_proof, + c1_proof, + c2a_proof, + c2b_proof, + c3a_inner_proofs, + c3b_inner_proofs, + c4a_proof, + c4b_proof, + c3_slot_indices_a: slots_a, + c3_slot_indices_b: slots_b, + c3_total_slots, + party_id, + params_preset: preset, }; - state.remaining -= 1; - let folds_completed = state.total_expected - state.remaining - 1; - let total_folds = state.total_expected.saturating_sub(1); + state.fold_correlation = Some(corr); + self.fold_correlation.insert(corr, e3_id.clone()); + info!( - "NodeProofAggregator: fold step {}/{} complete for E3 {} ({} proofs remaining)", - folds_completed, total_folds, e3_id, state.remaining + "NodeProofAggregator: dispatching NodeDkgFold for E3 {} party {}", + e3_id, party_id ); - state.accumulated = Some(proof); - state.fold_correlation = None; - - if state.remaining == 0 { - self.publish_complete(&e3_id); - } else { - self.try_advance(&e3_id); + if let Err(err) = self.bus.publish( + ComputeRequest::zk(ZkRequest::NodeDkgFold(req), corr, e3_id.clone()), + ec, + ) { + error!( + "NodeProofAggregator: failed to publish NodeDkgFold for E3 {}: {err}", + e3_id + ); + let _ = self.states.get_mut(e3_id).map(|s| { + s.fold_correlation = None; + }); + self.fold_correlation.remove(&corr); } } - fn publish_complete(&mut self, e3_id: &E3id) { - let Some(state) = self.states.remove(e3_id) else { + fn handle_node_dkg_response(&mut self, correlation_id: &CorrelationId, proof: Proof) { + let Some(e3_id) = self.fold_correlation.remove(correlation_id) else { return; }; - let Some(aggregated_proof) = state.accumulated else { + let Some(state) = self.states.remove(&e3_id) else { error!( - "NodeProofAggregator: no accumulated proof for E3 {} at completion", + "NodeProofAggregator: NodeDkgFold response for unknown E3 {}", e3_id ); return; }; info!( - "NodeProofAggregator: all proofs folded for E3 {} party {} — publishing DKGRecursiveAggregationComplete", - e3_id, state.party_id + "NodeProofAggregator: NodeDkgFold complete for E3 {} party {} — publishing DKGRecursiveAggregationComplete", + e3_id, state.meta.party_id ); if let Err(err) = self.bus.publish( DKGRecursiveAggregationComplete { e3_id: e3_id.clone(), - party_id: state.party_id, - aggregated_proof: Some(aggregated_proof), + party_id: state.meta.party_id, + aggregated_proof: Some(proof), }, state.last_ec, ) { @@ -385,8 +375,8 @@ impl Handler> for NodeProofAggregator { impl NodeProofAggregator { fn handle_compute_response(&mut self, msg: TypedEvent) { let (msg, _ec) = msg.into_components(); - if let ComputeResponseKind::Zk(ZkResponse::FoldProofs(resp)) = msg.response { - self.handle_fold_response(&msg.correlation_id, resp.proof); + if let ComputeResponseKind::Zk(ZkResponse::NodeDkgFold(resp)) = msg.response { + self.handle_node_dkg_response(&msg.correlation_id, resp.proof); } } @@ -394,13 +384,13 @@ impl NodeProofAggregator { let (msg, _ec) = msg.into_components(); if let Some(e3_id) = self.fold_correlation.remove(msg.correlation_id()) { error!( - "NodeProofAggregator: fold request failed for E3 {}: {:?} — aggregation aborted", + "NodeProofAggregator: NodeDkgFold failed for E3 {}: {:?} — aggregation aborted", e3_id, msg.get_err() ); self.states.remove(&e3_id); warn!( - "NodeProofAggregator: E3 {} aggregation state discarded due to fold error", + "NodeProofAggregator: E3 {} aggregation state discarded due to error", e3_id ); } diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index 638126bdc1..7895485bb9 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -49,9 +49,9 @@ enum ThresholdProofKind { struct NodeAggregationMeta { party_id: u64, total_expected: usize, - /// Buffered C0 wrapped proof, if it arrived before meta was stored. + /// Buffered C0 proof, if it arrived before meta was stored. pending_c0: Option, - /// When false, skip emitting DKGInnerProofReady (no wrapping/folding). + /// When false, skip emitting DKGInnerProofReady (no recursive DKG aggregation). proof_aggregation_enabled: bool, } @@ -264,15 +264,6 @@ impl ProofRequestActor { addr } - /// Returns true if proof aggregation (wrapping/folding) is enabled for this E3. - /// Defaults to `false` when metadata is missing (e.g. after restart) to avoid - /// requiring wrapped proofs that were never generated. - fn is_proof_aggregation_enabled(&self, e3_id: &E3id) -> bool { - self.node_agg_meta - .get(e3_id) - .map_or(false, |m| m.proof_aggregation_enabled) - } - fn handle_encryption_key_pending(&mut self, msg: TypedEvent) { let (msg, ec) = msg.into_components(); let correlation_id = CorrelationId::new(); @@ -321,14 +312,14 @@ impl ProofRequestActor { proof_aggregation_enabled: msg.proof_aggregation_enabled, }, ); - // If C0 wrapped proof arrived before meta, emit DKGInnerProofReady now + // If C0 proof arrived before meta, emit DKGInnerProofReady now if let Some(c0_proof) = pending_c0 { if msg.proof_aggregation_enabled { if let Err(err) = self.bus.publish( DKGInnerProofReady { e3_id: e3_id.clone(), party_id: msg.full_share.party_id, - wrapped_proof: c0_proof, + proof: c0_proof, seq: 0, }, ec.clone(), @@ -481,36 +472,16 @@ impl ProofRequestActor { let (msg, ec) = msg.into_components(); match &msg.response { ComputeResponseKind::Zk(ZkResponse::PkBfv(resp)) => { - self.handle_pk_bfv_response( - &msg.correlation_id, - resp.proof.clone(), - resp.wrapped_proof.clone(), - &ec, - ); + self.handle_pk_bfv_response(&msg.correlation_id, resp.proof.clone(), &ec); } ComputeResponseKind::Zk(ZkResponse::PkGeneration(resp)) => { - self.handle_threshold_proof_response( - &msg.correlation_id, - resp.proof.clone(), - resp.wrapped_proof.clone(), - &ec, - ); + self.handle_threshold_proof_response(&msg.correlation_id, resp.proof.clone(), &ec); } ComputeResponseKind::Zk(ZkResponse::ShareComputation(resp)) => { - self.handle_threshold_proof_response( - &msg.correlation_id, - resp.proof.clone(), - resp.wrapped_proof.clone(), - &ec, - ); + self.handle_threshold_proof_response(&msg.correlation_id, resp.proof.clone(), &ec); } ComputeResponseKind::Zk(ZkResponse::ShareEncryption(resp)) => { - self.handle_threshold_proof_response( - &msg.correlation_id, - resp.proof.clone(), - resp.wrapped_proof.clone(), - &ec, - ); + self.handle_threshold_proof_response(&msg.correlation_id, resp.proof.clone(), &ec); } ComputeResponseKind::Zk(ZkResponse::DkgShareDecryption(resp)) => { // Try C4 decryption proof first, then fall back to C1/C2/C3 threshold @@ -521,14 +492,12 @@ impl ProofRequestActor { self.handle_decryption_proof_response( &msg.correlation_id, resp.proof.clone(), - resp.wrapped_proof.clone(), &ec, ); } else { self.handle_threshold_proof_response( &msg.correlation_id, resp.proof.clone(), - resp.wrapped_proof.clone(), &ec, ); } @@ -537,7 +506,6 @@ impl ProofRequestActor { self.handle_share_decryption_proof_response( &msg.correlation_id, resp.proofs.clone(), - resp.wrapped_proofs.clone(), ); } ComputeResponseKind::Zk(ZkResponse::PkAggregation(resp)) => { @@ -646,7 +614,6 @@ impl ProofRequestActor { &mut self, correlation_id: &CorrelationId, proof: Proof, - wrapped_proof: Proof, ec: &EventContext, ) { let Some((e3_id, kind, seq)) = self.decryption_correlation.remove(correlation_id) else { @@ -661,6 +628,7 @@ impl ProofRequestActor { return; }; + let proof_for_agg = proof.clone(); match kind { DecryptionProofKind::SecretKey => { info!("Received C4a SK decryption proof for E3 {}", e3_id); @@ -681,7 +649,7 @@ impl ProofRequestActor { DKGInnerProofReady { e3_id: e3_id.clone(), party_id: meta.party_id, - wrapped_proof, + proof: proof_for_agg, seq, }, ec.clone(), @@ -809,12 +777,11 @@ impl ProofRequestActor { } } - /// Handle C6 proof response — sign raw proofs, keep wrapped for fold, publish DecryptionshareCreated. + /// Handle C6 proof response — sign proofs, publish DecryptionshareCreated. fn handle_share_decryption_proof_response( &mut self, correlation_id: &CorrelationId, proofs: Vec, - wrapped_proofs: Vec, ) { let Some(e3_id) = self.share_decryption_correlation.remove(correlation_id) else { return; @@ -828,16 +795,6 @@ impl ProofRequestActor { return; }; - let aggregation_enabled = self.is_proof_aggregation_enabled(&e3_id); - if aggregation_enabled && proofs.len() != wrapped_proofs.len() { - error!( - "C6 proofs and wrapped_proofs length mismatch: {} vs {} — DecryptionshareCreated will not be published", - proofs.len(), - wrapped_proofs.len() - ); - return; - } - // Sign raw C6 proofs (for ShareVerification) let mut signed_proofs = Vec::with_capacity(proofs.len()); for proof in proofs { @@ -859,12 +816,6 @@ impl ProofRequestActor { let ec = pending.ec; - let wrapped_to_publish = if aggregation_enabled { - wrapped_proofs - } else { - Vec::new() - }; - match self.bus.publish( DecryptionshareCreated { party_id: pending.party_id, @@ -872,7 +823,6 @@ impl ProofRequestActor { e3_id: e3_id.clone(), decryption_share: pending.decryption_share, signed_decryption_proofs: signed_proofs, - wrapped_proofs: wrapped_to_publish, }, ec.clone(), ) { @@ -1061,7 +1011,6 @@ impl ProofRequestActor { &mut self, correlation_id: &CorrelationId, proof: Proof, - wrapped_proof: Proof, ec: &EventContext, ) { let Some((e3_id, kind, seq)) = self.threshold_correlation.remove(correlation_id) else { @@ -1076,6 +1025,7 @@ impl ProofRequestActor { return; }; + let proof_for_agg = proof.clone(); pending.store_proof(&kind, proof); info!( "Received {:?} proof for E3 {} ({}/{})", @@ -1091,7 +1041,7 @@ impl ProofRequestActor { DKGInnerProofReady { e3_id: e3_id.clone(), party_id: meta.party_id, - wrapped_proof, + proof: proof_for_agg, seq, }, ec.clone(), @@ -1329,7 +1279,6 @@ impl ProofRequestActor { &mut self, correlation_id: &CorrelationId, proof: Proof, - wrapped_proof: Proof, ec: &EventContext, ) { let Some(pending) = self.pending.remove(&correlation_id) else { @@ -1413,7 +1362,7 @@ impl ProofRequestActor { DKGInnerProofReady { e3_id: e3_id.clone(), party_id: meta.party_id, - wrapped_proof: wrapped_proof.clone(), + proof: proof.clone(), seq: 0, }, ec.clone(), @@ -1422,13 +1371,13 @@ impl ProofRequestActor { } } } else { - // ThresholdSharePending hasn't arrived yet — buffer C0 wrapped proof + // ThresholdSharePending hasn't arrived yet — buffer C0 proof self.node_agg_meta.insert( e3_id.clone(), NodeAggregationMeta { party_id: 0, total_expected: 0, - pending_c0: Some(wrapped_proof), + pending_c0: Some(proof), proof_aggregation_enabled: true, // will be overwritten by ThresholdSharePending }, ); diff --git a/crates/zk-prover/src/circuits/aggregation/c3_accumulator.rs b/crates/zk-prover/src/circuits/aggregation/c3_accumulator.rs new file mode 100644 index 0000000000..85319b1983 --- /dev/null +++ b/crates/zk-prover/src/circuits/aggregation/c3_accumulator.rs @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Sequential C3 fold: each step verifies one inner `ShareEncryption` proof and the accumulator +//! (`c3_fold` non-ZK proof). The first step proves [`CircuitName::C3FoldKernel`] at runtime to obtain +//! a valid genesis `UltraHonkProof` (see `circuits/bin/recursive_aggregation/c3_fold_kernel`). +//! +//! Ciphernodes integrate via [`generate_sequential_c3_fold`] only: they supply the full list of C3 +//! inner proofs and slot indices; per-step folding is not exposed outside this crate. + +use crate::circuits::aggregation::helpers::{ + extract_single_field, field_keys, parse_acc_public_field_strings, sequential_fold, + zero_field_hex_strings, ACC_NONZK_PROOF_FIELDS, +}; +use crate::circuits::utils::{bytes_to_field_strings, inputs_json_to_input_map}; +use crate::circuits::vk; +use crate::error::ZkError; +use crate::prover::ZkProver; +use crate::witness::{CompiledCircuit, WitnessGenerator}; +use e3_events::{CircuitName, CircuitVariant, Proof}; +use serde::Serialize; + +/// `total_slots` = N_PARTIES * L_THRESHOLD (one slot per party-modulus pair). +fn c3_fold_public_input_field_count(total_slots: usize) -> usize { + 4 + 3 * total_slots +} + +/// Public-signal layout of `c3_fold`: 4-field prefix, then 3-field-wide per-slot tail. +const C3_FOLD_PREFIX_LEN: usize = 4; +const C3_FOLD_SLOT_WIDTH: usize = 3; + +/// Proves [`CircuitName::C3FoldKernel`] for the same `inner` / `total_slots` as the fold step. +/// +/// Uses work dir `job_id` (caller should use a suffix of the fold `e3_id` so jobs stay distinct). +/// Removes that work dir after the proof is returned. +fn generate_c3_fold_kernel_genesis_proof( + prover: &ZkProver, + inner: &Proof, + total_slots: usize, + artifacts_dir: &str, + job_id: &str, +) -> Result { + let inner_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::ShareEncryption, + )?; + let kernel_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C3FoldKernel, + )?; + let c3_public_inputs = share_encryption_inner_public_inputs(inner)?; + let expected_acc_pub = c3_fold_public_input_field_count(total_slots); + let acc_pi = zero_field_hex_strings(expected_acc_pub)?; + let acc_pf = zero_field_hex_strings(ACC_NONZK_PROOF_FIELDS)?; + + let full_input = C3FoldStepInput { + inner_vk: inner_vk.verification_key, + inner_proof: bytes_to_field_strings(&inner.data)?, + c3_public_inputs, + acc_vk: kernel_vk.verification_key, + acc_proof: acc_pf, + acc_public_inputs: acc_pi, + inner_key_hash: inner_vk.key_hash, + acc_key_hash: kernel_vk.key_hash, + is_first_step: true, + slot_index: 0, + }; + + let circuit_path = prover + .circuits_dir(CircuitVariant::Default, artifacts_dir) + .join(CircuitName::C3FoldKernel.dir_path()) + .join(format!("{}.json", CircuitName::C3FoldKernel.as_str())); + let compiled = CompiledCircuit::from_file(&circuit_path)?; + + let json = serde_json::to_value(&full_input) + .map_err(|e| ZkError::SerializationError(e.to_string()))?; + let input_map = inputs_json_to_input_map(&json)?; + let witness_gen = WitnessGenerator::new(); + let witness = witness_gen.generate_witness(&compiled, input_map)?; + + let proof = prover.generate_recursive_aggregation_bin_proof( + CircuitName::C3FoldKernel, + &witness, + job_id, + artifacts_dir, + )?; + let _ = prover.cleanup(job_id); + Ok(proof) +} + +/// Inner C3 public transcript: two inputs + `ct_commitment` output. +fn share_encryption_inner_public_inputs(proof: &Proof) -> Result<[String; 3], ZkError> { + if proof.circuit != CircuitName::ShareEncryption { + return Err(ZkError::InvalidInput(format!( + "expected ShareEncryption inner proof, got {}", + proof.circuit + ))); + } + let ctx = "C3 inner ShareEncryption proof"; + Ok([ + extract_single_field(proof, "input", field_keys::EXPECTED_PK_COMMITMENT, ctx)?, + extract_single_field(proof, "input", field_keys::EXPECTED_MESSAGE_COMMITMENT, ctx)?, + extract_single_field(proof, "output", field_keys::CT_COMMITMENT, ctx)?, + ]) +} + +#[derive(Serialize)] +struct C3FoldStepInput { + inner_vk: Vec, + inner_proof: Vec, + c3_public_inputs: [String; 3], + acc_vk: Vec, + acc_proof: Vec, + acc_public_inputs: Vec, + inner_key_hash: String, + acc_key_hash: String, + is_first_step: bool, + slot_index: u32, +} + +fn parse_c3_fold_public_field_strings(proof: &Proof) -> Result, ZkError> { + parse_acc_public_field_strings( + proof, + CircuitName::C3Fold, + C3_FOLD_PREFIX_LEN, + C3_FOLD_SLOT_WIDTH, + ) +} + +/// One sequential `c3_fold` step. +/// +/// `prior_fold` is `None` on the first step and `Some` on all subsequent steps. +/// `total_slots` is `N_PARTIES * L_THRESHOLD` — one slot per (party, threshold-modulus) pair. +/// On the first step this sets the accumulator size; on subsequent steps it is cross-checked +/// against the slot count already encoded in `prior_fold`. +/// +/// Used only by [`generate_sequential_c3_fold`]; callers should use that entry point. +fn generate_c3_fold_step( + prover: &ZkProver, + inner: &Proof, + prior_fold: Option<&Proof>, + slot_index: u32, + total_slots: usize, + e3_id: &str, + artifacts_dir: &str, +) -> Result { + let is_first_step = prior_fold.is_none(); + + let inner_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::ShareEncryption, + )?; + let c3_public_inputs = share_encryption_inner_public_inputs(inner)?; + + let expected_acc_pub = c3_fold_public_input_field_count(total_slots); + + let (acc_vk_art, acc_proof, acc_public_inputs) = if is_first_step { + let kernel_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C3FoldKernel, + )?; + let kernel_job_id = format!("{e3_id}-c3fold-kernel"); + let kernel_proof = generate_c3_fold_kernel_genesis_proof( + prover, + inner, + total_slots, + artifacts_dir, + &kernel_job_id, + )?; + let acc_pi = bytes_to_field_strings(kernel_proof.public_signals.as_ref())?; + if acc_pi.len() != expected_acc_pub { + return Err(ZkError::InvalidInput(format!( + "c3_fold kernel proof public_inputs field count {} != expected {} (total_slots={})", + acc_pi.len(), + expected_acc_pub, + total_slots + ))); + } + ( + kernel_vk, + bytes_to_field_strings(&kernel_proof.data)?, + acc_pi, + ) + } else { + let p = prior_fold.expect("prior_fold required when is_first_step is false"); + // Parse once; derive slot count from field count to avoid a second parse. + let acc_pi = parse_c3_fold_public_field_strings(p)?; + let prior_slots = (acc_pi.len() - 4) / 3; + if prior_slots == 0 { + return Err(ZkError::InvalidInput( + "c3_fold proof implies zero slots".into(), + )); + } + if prior_slots != total_slots { + return Err(ZkError::InvalidInput(format!( + "prior c3_fold slot count {} != expected {}", + prior_slots, total_slots + ))); + } + if acc_pi.len() != expected_acc_pub { + return Err(ZkError::InvalidInput(format!( + "prior c3_fold public field count {} != expected {} for total_slots={}", + acc_pi.len(), + expected_acc_pub, + total_slots + ))); + } + ( + vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C3Fold, + )?, + bytes_to_field_strings(&p.data)?, + acc_pi, + ) + }; + + let full_input = C3FoldStepInput { + inner_vk: inner_vk.verification_key, + inner_proof: bytes_to_field_strings(&inner.data)?, + c3_public_inputs, + acc_vk: acc_vk_art.verification_key, + acc_proof, + acc_public_inputs, + inner_key_hash: inner_vk.key_hash, + acc_key_hash: acc_vk_art.key_hash, + is_first_step, + slot_index, + }; + + let circuit_path = prover + .circuits_dir(CircuitVariant::Default, artifacts_dir) + .join(CircuitName::C3Fold.dir_path()) + .join(format!("{}.json", CircuitName::C3Fold.as_str())); + let compiled = CompiledCircuit::from_file(&circuit_path)?; + + let json = serde_json::to_value(&full_input) + .map_err(|e| ZkError::SerializationError(e.to_string()))?; + let input_map = inputs_json_to_input_map(&json)?; + + let witness_gen = WitnessGenerator::new(); + let witness = witness_gen.generate_witness(&compiled, input_map)?; + + prover.generate_recursive_aggregation_bin_proof( + CircuitName::C3Fold, + &witness, + e3_id, + artifacts_dir, + ) +} + +/// Folds `inner_proofs` in order, one inner C3 proof per step — the integration surface for +/// ciphernodes (batch in, single `C3Fold` proof out). +/// +/// `slot_indices[i]` is the `(party * L_THRESHOLD + modulus)` slot for `inner_proofs[i]`. +/// `total_slots` must equal `N_PARTIES * L_THRESHOLD` and determines the accumulator size. +pub fn generate_sequential_c3_fold( + prover: &ZkProver, + inner_proofs: &[Proof], + slot_indices: &[u32], + total_slots: usize, + e3_id: &str, + artifacts_dir: &str, +) -> Result { + // Defense in depth: every slot index must be in range and used at most once. C3 chains are + // allowed to be partial (one C3 per (recipient, modulus) actually computed by this party), + // unlike C6 which must cover all `T + 1` slots, so length equality is intentionally not + // enforced here. + if inner_proofs.len() != slot_indices.len() { + return Err(ZkError::InvalidInput(format!( + "generate_sequential_c3_fold: inner_proofs and slot_indices length mismatch ({} vs {})", + inner_proofs.len(), + slot_indices.len() + ))); + } + let mut seen = vec![false; total_slots]; + for &s in slot_indices { + let idx = s as usize; + if idx >= total_slots { + return Err(ZkError::InvalidInput(format!( + "generate_sequential_c3_fold: slot index {s} out of range (total_slots={total_slots})" + ))); + } + if seen[idx] { + return Err(ZkError::InvalidInput(format!( + "generate_sequential_c3_fold: duplicate slot index {s}" + ))); + } + seen[idx] = true; + } + sequential_fold( + "generate_sequential_c3_fold", + inner_proofs, + slot_indices, + |inner, prior, slot| { + generate_c3_fold_step( + prover, + inner, + prior, + slot, + total_slots, + e3_id, + artifacts_dir, + ) + }, + ) +} diff --git a/crates/zk-prover/src/circuits/aggregation/c6_accumulator.rs b/crates/zk-prover/src/circuits/aggregation/c6_accumulator.rs new file mode 100644 index 0000000000..3c7fcb4a87 --- /dev/null +++ b/crates/zk-prover/src/circuits/aggregation/c6_accumulator.rs @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Sequential C6 fold: each step verifies one inner `ThresholdShareDecryption` proof and the +//! accumulator (`c6_fold` non-ZK proof). The first step proves [`CircuitName::C6FoldKernel`] at +//! runtime to obtain a valid genesis `UltraHonkProof` (see `circuits/bin/recursive_aggregation/c6_fold_kernel`). + +use crate::circuits::aggregation::helpers::{ + extract_single_field, field_keys, parse_acc_public_field_strings, sequential_fold, + zero_field_hex_strings, ACC_NONZK_PROOF_FIELDS, +}; +use crate::circuits::utils::{bytes_to_field_strings, inputs_json_to_input_map}; +use crate::circuits::vk; +use crate::error::ZkError; +use crate::prover::ZkProver; +use crate::witness::{CompiledCircuit, WitnessGenerator}; +use e3_events::{CircuitName, CircuitVariant, Proof}; +use serde::Serialize; + +/// `total_slots` = `T + 1` (one slot per party index in the C6 leaf layout). +fn c6_fold_public_input_field_count(total_slots: usize) -> usize { + 4 + 4 * total_slots +} + +/// Public-signal layout of `c6_fold`: 4-field prefix, then 4-field-wide per-slot tail. +const C6_FOLD_PREFIX_LEN: usize = 4; +const C6_FOLD_SLOT_WIDTH: usize = 4; + +fn generate_c6_fold_kernel_genesis_proof( + prover: &ZkProver, + inner: &Proof, + slot_index: u32, + total_slots: usize, + artifacts_dir: &str, + job_id: &str, +) -> Result { + let inner_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::ThresholdShareDecryption, + )?; + let kernel_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C6FoldKernel, + )?; + let c6_public_inputs = threshold_share_decryption_inner_public_inputs(inner)?; + let expected_acc_pub = c6_fold_public_input_field_count(total_slots); + let acc_pi = zero_field_hex_strings(expected_acc_pub)?; + let acc_pf = zero_field_hex_strings(ACC_NONZK_PROOF_FIELDS)?; + + let full_input = C6FoldStepInput { + inner_vk: inner_vk.verification_key, + inner_proof: bytes_to_field_strings(&inner.data)?, + c6_public_inputs, + acc_vk: kernel_vk.verification_key, + acc_proof: acc_pf, + acc_public_inputs: acc_pi, + inner_key_hash: inner_vk.key_hash, + acc_key_hash: kernel_vk.key_hash, + is_first_step: true, + slot_index, + }; + + let circuit_path = prover + .circuits_dir(CircuitVariant::Default, artifacts_dir) + .join(CircuitName::C6FoldKernel.dir_path()) + .join(format!("{}.json", CircuitName::C6FoldKernel.as_str())); + let compiled = CompiledCircuit::from_file(&circuit_path)?; + + let json = serde_json::to_value(&full_input) + .map_err(|e| ZkError::SerializationError(e.to_string()))?; + let input_map = inputs_json_to_input_map(&json)?; + let witness_gen = WitnessGenerator::new(); + let witness = witness_gen.generate_witness(&compiled, input_map)?; + + let proof = prover.generate_recursive_aggregation_bin_proof( + CircuitName::C6FoldKernel, + &witness, + job_id, + artifacts_dir, + )?; + let _ = prover.cleanup(job_id); + Ok(proof) +} + +fn threshold_share_decryption_inner_public_inputs(proof: &Proof) -> Result<[String; 4], ZkError> { + if proof.circuit != CircuitName::ThresholdShareDecryption { + return Err(ZkError::InvalidInput(format!( + "expected ThresholdShareDecryption inner proof, got {}", + proof.circuit + ))); + } + let ctx = "C6 inner ThresholdShareDecryption proof"; + Ok([ + extract_single_field(proof, "input", field_keys::EXPECTED_SK_COMMITMENT, ctx)?, + extract_single_field(proof, "input", field_keys::EXPECTED_E_SM_COMMITMENT, ctx)?, + extract_single_field(proof, "input", field_keys::CT_COMMITMENT, ctx)?, + extract_single_field(proof, "output", field_keys::D_COMMITMENT, ctx)?, + ]) +} + +#[derive(Serialize)] +struct C6FoldStepInput { + inner_vk: Vec, + inner_proof: Vec, + c6_public_inputs: [String; 4], + acc_vk: Vec, + acc_proof: Vec, + acc_public_inputs: Vec, + inner_key_hash: String, + acc_key_hash: String, + is_first_step: bool, + slot_index: u32, +} + +fn parse_c6_fold_public_field_strings(proof: &Proof) -> Result, ZkError> { + parse_acc_public_field_strings( + proof, + CircuitName::C6Fold, + C6_FOLD_PREFIX_LEN, + C6_FOLD_SLOT_WIDTH, + ) +} + +fn generate_c6_fold_step( + prover: &ZkProver, + inner: &Proof, + prior_fold: Option<&Proof>, + slot_index: u32, + total_slots: usize, + e3_id: &str, + artifacts_dir: &str, +) -> Result { + let is_first_step = prior_fold.is_none(); + + let inner_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::ThresholdShareDecryption, + )?; + let c6_public_inputs = threshold_share_decryption_inner_public_inputs(inner)?; + + let expected_acc_pub = c6_fold_public_input_field_count(total_slots); + + let (acc_vk_art, acc_proof, acc_public_inputs) = if is_first_step { + let kernel_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C6FoldKernel, + )?; + let kernel_job_id = format!("{e3_id}-c6fold-kernel"); + let kernel_proof = generate_c6_fold_kernel_genesis_proof( + prover, + inner, + slot_index, + total_slots, + artifacts_dir, + &kernel_job_id, + )?; + let acc_pi = bytes_to_field_strings(kernel_proof.public_signals.as_ref())?; + if acc_pi.len() != expected_acc_pub { + return Err(ZkError::InvalidInput(format!( + "c6_fold kernel proof public_inputs field count {} != expected {} (total_slots={})", + acc_pi.len(), + expected_acc_pub, + total_slots + ))); + } + ( + kernel_vk, + bytes_to_field_strings(&kernel_proof.data)?, + acc_pi, + ) + } else { + let p = prior_fold.expect("prior_fold required when is_first_step is false"); + let acc_pi = parse_c6_fold_public_field_strings(p)?; + let prior_slots = (acc_pi.len() - 4) / 4; + if prior_slots == 0 { + return Err(ZkError::InvalidInput( + "c6_fold proof implies zero slots".into(), + )); + } + if prior_slots != total_slots { + return Err(ZkError::InvalidInput(format!( + "prior c6_fold slot count {} != expected {}", + prior_slots, total_slots + ))); + } + if acc_pi.len() != expected_acc_pub { + return Err(ZkError::InvalidInput(format!( + "prior c6_fold public field count {} != expected {} for total_slots={}", + acc_pi.len(), + expected_acc_pub, + total_slots + ))); + } + ( + vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C6Fold, + )?, + bytes_to_field_strings(&p.data)?, + acc_pi, + ) + }; + + let full_input = C6FoldStepInput { + inner_vk: inner_vk.verification_key, + inner_proof: bytes_to_field_strings(&inner.data)?, + c6_public_inputs, + acc_vk: acc_vk_art.verification_key, + acc_proof, + acc_public_inputs, + inner_key_hash: inner_vk.key_hash, + acc_key_hash: acc_vk_art.key_hash, + is_first_step, + slot_index, + }; + + let circuit_path = prover + .circuits_dir(CircuitVariant::Default, artifacts_dir) + .join(CircuitName::C6Fold.dir_path()) + .join(format!("{}.json", CircuitName::C6Fold.as_str())); + let compiled = CompiledCircuit::from_file(&circuit_path)?; + + let json = serde_json::to_value(&full_input) + .map_err(|e| ZkError::SerializationError(e.to_string()))?; + let input_map = inputs_json_to_input_map(&json)?; + + let witness_gen = WitnessGenerator::new(); + let witness = witness_gen.generate_witness(&compiled, input_map)?; + + prover.generate_recursive_aggregation_bin_proof( + CircuitName::C6Fold, + &witness, + e3_id, + artifacts_dir, + ) +} + +/// Folds `inner_proofs` in order, one inner C6 proof per step — the integration surface for +/// ciphernodes (batch in, single `C6Fold` proof out). +/// +/// `slot_indices[i]` is the party slot index for `inner_proofs[i]`. +/// `total_slots` must equal `T + 1` and determines the accumulator width. +pub fn generate_sequential_c6_fold( + prover: &ZkProver, + inner_proofs: &[Proof], + slot_indices: &[u32], + total_slots: usize, + e3_id: &str, + artifacts_dir: &str, +) -> Result { + // Defense in depth: every C6 slot must be filled exactly once and indices must be in range. + // Without this a partial fold or duplicate slot could reach the decryption aggregator. + if inner_proofs.len() != total_slots { + return Err(ZkError::InvalidInput(format!( + "generate_sequential_c6_fold: expected {total_slots} inner proofs, got {}", + inner_proofs.len() + ))); + } + let mut seen = vec![false; total_slots]; + for &s in slot_indices { + let idx = s as usize; + if idx >= total_slots { + return Err(ZkError::InvalidInput(format!( + "generate_sequential_c6_fold: slot index {s} out of range (total_slots={total_slots})" + ))); + } + if seen[idx] { + return Err(ZkError::InvalidInput(format!( + "generate_sequential_c6_fold: duplicate slot index {s}" + ))); + } + seen[idx] = true; + } + sequential_fold( + "generate_sequential_c6_fold", + inner_proofs, + slot_indices, + |inner, prior, slot| { + generate_c6_fold_step( + prover, + inner, + prior, + slot, + total_slots, + e3_id, + artifacts_dir, + ) + }, + ) +} diff --git a/crates/zk-prover/src/circuits/aggregation/helpers.rs b/crates/zk-prover/src/circuits/aggregation/helpers.rs new file mode 100644 index 0000000000..e8e4be89cd --- /dev/null +++ b/crates/zk-prover/src/circuits/aggregation/helpers.rs @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Shared helpers for the sequential `c3_fold` / `c6_fold` / `nodes_fold` accumulators and +//! the single-shot `node_dkg_fold` builder. + +use crate::circuits::utils::bytes_to_field_strings; +use crate::error::ZkError; +use e3_events::{CircuitName, Proof}; + +/// Field count for `UltraHonkProof` (non-ZK) used as the `acc_proof` parameter in every +/// recursive fold step. Sourced from `nargo compile` ABI. +pub const ACC_NONZK_PROOF_FIELDS: usize = 457; + +/// String keys for inner-circuit public input/output extraction. Centralised so a +/// rename in the Noir ABI surfaces as a single edit instead of a runtime panic. +/// +/// These names must stay in lock-step with the corresponding Noir circuit ABI +/// declarations (e.g. `expected_pk_commitment` is declared in C3 / `share_encryption.nr`). +pub mod field_keys { + pub const EXPECTED_PK_COMMITMENT: &str = "expected_pk_commitment"; + pub const EXPECTED_MESSAGE_COMMITMENT: &str = "expected_message_commitment"; + pub const EXPECTED_SK_COMMITMENT: &str = "expected_sk_commitment"; + pub const EXPECTED_E_SM_COMMITMENT: &str = "expected_e_sm_commitment"; + pub const CT_COMMITMENT: &str = "ct_commitment"; + pub const D_COMMITMENT: &str = "d_commitment"; +} + +/// Vector of `field_count` zero-encoded 32-byte hex field strings for the genesis accumulator. +pub fn zero_field_hex_strings(field_count: usize) -> Result, ZkError> { + let bytes = vec![0u8; field_count * 32]; + bytes_to_field_strings(&bytes) +} + +/// Encode a `u64` as a 32-byte field hex string (big-endian, left-padded). +pub fn u64_to_field_hex(value: u64) -> String { + let mut bytes = [0u8; 32]; + bytes[24..].copy_from_slice(&value.to_be_bytes()); + format!("0x{}", hex::encode(bytes)) +} + +/// Extract a single 32-byte public input/output by name. `kind` must be `"input"` or `"output"`. +pub fn extract_single_field( + proof: &Proof, + kind: &str, + name: &str, + context: &str, +) -> Result { + let bytes = match kind { + "input" => proof + .extract_input(name) + .ok_or_else(|| ZkError::InvalidInput(format!("{context}: missing input {name}")))?, + "output" => proof + .extract_output(name) + .ok_or_else(|| ZkError::InvalidInput(format!("{context}: missing output {name}")))?, + _ => { + return Err(ZkError::InvalidInput(format!( + "extract_single_field: kind must be input or output, got {kind}" + ))); + } + }; + let fields = bytes_to_field_strings(bytes.as_ref())?; + if fields.len() != 1 { + return Err(ZkError::InvalidInput(format!( + "{context}: field {name} encoded as {} fields, expected 1", + fields.len() + ))); + } + Ok(fields.into_iter().next().expect("len == 1 verified above")) +} + +/// Parse and validate fold-accumulator public-signal field strings against +/// `(prefix_len, slot_width)`. Returns the flattened field-string vector. +/// +/// Use this when the accumulator has a per-slot tail of fixed `slot_width` (e.g. `c3_fold`, +/// `c6_fold`). For accumulators whose tail layout is determined at runtime by the inner proof +/// shape (e.g. `nodes_fold`, where each slot stores a whole `node_fold` statement), use +/// [`parse_acc_public_field_strings_flat`] and perform the per-slot length check at the call site. +pub fn parse_acc_public_field_strings( + proof: &Proof, + expected_circuit: CircuitName, + prefix_len: usize, + slot_width: usize, +) -> Result, ZkError> { + if proof.circuit != expected_circuit { + return Err(ZkError::InvalidInput(format!( + "expected {expected_circuit} proof, got {}", + proof.circuit + ))); + } + let v = bytes_to_field_strings(proof.public_signals.as_ref())?; + if v.len() < prefix_len || (v.len() - prefix_len) % slot_width != 0 { + return Err(ZkError::InvalidInput(format!( + "unexpected {expected_circuit} public signal field count: {} (prefix={prefix_len}, slot_width={slot_width})", + v.len() + ))); + } + Ok(v) +} + +/// Same as [`parse_acc_public_field_strings`] but only validates the prefix length +/// (no slot-width modulo check). Intended for accumulators whose per-slot width is determined +/// at runtime from the inner proof shape; the caller is responsible for verifying the total +/// length against the expected `prefix_len + slots * `. +pub fn parse_acc_public_field_strings_flat( + proof: &Proof, + expected_circuit: CircuitName, + prefix_len: usize, +) -> Result, ZkError> { + if proof.circuit != expected_circuit { + return Err(ZkError::InvalidInput(format!( + "expected {expected_circuit} proof, got {}", + proof.circuit + ))); + } + let v = bytes_to_field_strings(proof.public_signals.as_ref())?; + if v.len() < prefix_len { + return Err(ZkError::InvalidInput(format!( + "unexpected {expected_circuit} public signal field count: {} (prefix={prefix_len})", + v.len() + ))); + } + Ok(v) +} + +/// Sequential fold driver: applies `step(inner, prior_acc, slot_index)` per inner proof, +/// threading each step's output as the next step's accumulator. +pub fn sequential_fold( + label: &str, + inner_proofs: &[Proof], + slot_indices: &[u32], + mut step: F, +) -> Result +where + F: FnMut(&Proof, Option<&Proof>, u32) -> Result, +{ + if inner_proofs.is_empty() { + return Err(ZkError::InvalidInput(format!( + "{label}: need at least one inner proof" + ))); + } + if inner_proofs.len() != slot_indices.len() { + return Err(ZkError::InvalidInput(format!( + "{label}: inner_proofs and slot_indices length mismatch ({} vs {})", + inner_proofs.len(), + slot_indices.len() + ))); + } + let mut acc: Option = None; + for (i, inner) in inner_proofs.iter().enumerate() { + acc = Some(step(inner, acc.as_ref(), slot_indices[i])?); + } + Ok(acc.expect("loop body executed at least once on non-empty input")) +} diff --git a/crates/zk-prover/src/circuits/aggregation/mod.rs b/crates/zk-prover/src/circuits/aggregation/mod.rs new file mode 100644 index 0000000000..4fe1ac5f59 --- /dev/null +++ b/crates/zk-prover/src/circuits/aggregation/mod.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Recursive proof aggregation (Noir fold / `recursive_aggregation` binaries). + +pub mod c3_accumulator; +pub mod c6_accumulator; +pub mod helpers; +pub mod node_dkg_fold; +pub mod nodes_fold_accumulator; diff --git a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs new file mode 100644 index 0000000000..0c352bbb03 --- /dev/null +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Production witness builders and provers for the per-node DKG fold pipeline and aggregator +//! proofs ([`CircuitName::NodeFold`], [`CircuitName::DkgAggregator`], [`CircuitName::DecryptionAggregator`]). + +use crate::circuits::aggregation::c3_accumulator::generate_sequential_c3_fold; +use crate::circuits::aggregation::c6_accumulator::generate_sequential_c6_fold; +use crate::circuits::aggregation::helpers::u64_to_field_hex; +use crate::circuits::aggregation::nodes_fold_accumulator::generate_sequential_nodes_fold; +use crate::circuits::utils::{bytes_to_field_strings, inputs_json_to_input_map}; +use crate::circuits::vk; +use crate::error::ZkError; +use crate::prover::ZkProver; +use crate::witness::{CompiledCircuit, WitnessGenerator}; +use e3_events::{CircuitName, CircuitVariant, Proof}; +use serde::Serialize; + +fn proof_field_strings(proof: &Proof) -> Result, ZkError> { + bytes_to_field_strings(proof.data.as_ref()) +} + +fn proof_public_field_strings(proof: &Proof) -> Result, ZkError> { + bytes_to_field_strings(proof.public_signals.as_ref()) +} + +/// Load `///.json` as a [`CompiledCircuit`]. Centralised +/// so the layout is in one place across all aggregation builders below. +fn load_compiled_circuit( + prover: &ZkProver, + circuit: CircuitName, + variant: CircuitVariant, + artifacts_dir: &str, +) -> Result { + let path = prover + .circuits_dir(variant, artifacts_dir) + .join(circuit.dir_path()) + .join(format!("{}.json", circuit.as_str())); + CompiledCircuit::from_file(&path) +} + +/// Build a witness from a `Serialize` Noir input struct and prove `circuit` via the recursive-bin +/// path. Collapses the repeated `serde_json::to_value → inputs_json_to_input_map → CompiledCircuit::from_file +/// → WitnessGenerator → generate_recursive_aggregation_bin_proof` boilerplate used by every fold +/// builder (c2ab / c3ab / c4ab / node_fold) in this module. +fn build_and_prove_recursive_bin( + prover: &ZkProver, + circuit: CircuitName, + witness_input: &W, + job_label: &str, + artifacts_dir: &str, +) -> Result { + let json = serde_json::to_value(witness_input) + .map_err(|e| ZkError::SerializationError(e.to_string()))?; + let input_map = inputs_json_to_input_map(&json)?; + let compiled = load_compiled_circuit(prover, circuit, CircuitVariant::Default, artifacts_dir)?; + let witness = WitnessGenerator::new().generate_witness(&compiled, input_map)?; + prover.generate_recursive_aggregation_bin_proof(circuit, &witness, job_label, artifacts_dir) +} + +#[derive(Serialize)] +struct C2abFoldWitness { + c2a_vk: Vec, + c2a_proof: Vec, + c2a_public: Vec, + c2b_vk: Vec, + c2b_proof: Vec, + c2b_public: Vec, + c2a_key_hash: String, + c2b_key_hash: String, +} + +#[derive(Serialize)] +struct C3abFoldWitness { + c3a_vk: Vec, + c3a_proof: Vec, + c3a_public: Vec, + c3b_vk: Vec, + c3b_proof: Vec, + c3b_public: Vec, + c3a_key_hash: String, + c3b_key_hash: String, +} + +#[derive(Serialize)] +struct C4abFoldWitness { + c4a_vk: Vec, + c4a_proof: Vec, + c4a_public: Vec, + c4b_vk: Vec, + c4b_proof: Vec, + c4b_public: Vec, + c4a_key_hash: String, + c4b_key_hash: String, +} + +#[derive(Serialize)] +struct NodeFoldWitness { + c0_vk: Vec, + c0_proof: Vec, + c0_public: Vec, + c1_vk: Vec, + c1_proof: Vec, + c1_public: Vec, + c2ab_vk: Vec, + c2ab_proof: Vec, + c2ab_public: Vec, + c3ab_vk: Vec, + c3ab_proof: Vec, + c3ab_public: Vec, + c4ab_vk: Vec, + c4ab_proof: Vec, + c4ab_public: Vec, + party_id: String, + c0_key_hash: String, + c1_key_hash: String, + c2ab_key_hash: String, + c3ab_key_hash: String, + c4ab_key_hash: String, +} + +/// Inputs for [`prove_node_dkg_fold`]: recursive inner proofs and C3 slot metadata. +pub struct NodeDkgFoldInput<'a> { + pub c0_proof: &'a Proof, + pub c1_proof: &'a Proof, + pub c2a_proof: &'a Proof, + pub c2b_proof: &'a Proof, + pub c3a_inner_proofs: &'a [Proof], + pub c3b_inner_proofs: &'a [Proof], + pub c3_slot_indices_a: &'a [u32], + pub c3_slot_indices_b: &'a [u32], + pub c3_total_slots: usize, + pub c4a_proof: &'a Proof, + pub c4b_proof: &'a Proof, + pub party_id: u64, +} + +/// Run C2abFold → C3 folds → C3abFold → C4abFold → NodeFold; returns a [`CircuitName::NodeFold`] proof. +pub fn prove_node_dkg_fold( + prover: &ZkProver, + input: &NodeDkgFoldInput, + e3_id: &str, + artifacts_dir: &str, +) -> Result { + let c2a_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::SkShareComputation, + )?; + let c2b_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::ESmShareComputation, + )?; + + let c2ab = C2abFoldWitness { + c2a_vk: c2a_vk.verification_key.clone(), + c2a_proof: proof_field_strings(input.c2a_proof)?, + c2a_public: proof_public_field_strings(input.c2a_proof)?, + c2b_vk: c2b_vk.verification_key.clone(), + c2b_proof: proof_field_strings(input.c2b_proof)?, + c2b_public: proof_public_field_strings(input.c2b_proof)?, + c2a_key_hash: c2a_vk.key_hash.clone(), + c2b_key_hash: c2b_vk.key_hash.clone(), + }; + let c2ab_proof = build_and_prove_recursive_bin( + prover, + CircuitName::C2abFold, + &c2ab, + &format!("{e3_id}-c2ab"), + artifacts_dir, + )?; + + let c3a_folded = generate_sequential_c3_fold( + prover, + input.c3a_inner_proofs, + input.c3_slot_indices_a, + input.c3_total_slots, + &format!("{e3_id}-c3a"), + artifacts_dir, + )?; + let c3b_folded = generate_sequential_c3_fold( + prover, + input.c3b_inner_proofs, + input.c3_slot_indices_b, + input.c3_total_slots, + &format!("{e3_id}-c3b"), + artifacts_dir, + )?; + + let c3_fold_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C3Fold, + )?; + let c3ab = C3abFoldWitness { + c3a_vk: c3_fold_vk.verification_key.clone(), + c3a_proof: proof_field_strings(&c3a_folded)?, + c3a_public: proof_public_field_strings(&c3a_folded)?, + c3b_vk: c3_fold_vk.verification_key.clone(), + c3b_proof: proof_field_strings(&c3b_folded)?, + c3b_public: proof_public_field_strings(&c3b_folded)?, + c3a_key_hash: c3_fold_vk.key_hash.clone(), + c3b_key_hash: c3_fold_vk.key_hash.clone(), + }; + let c3ab_proof = build_and_prove_recursive_bin( + prover, + CircuitName::C3abFold, + &c3ab, + &format!("{e3_id}-c3ab"), + artifacts_dir, + )?; + + // C4a and C4b are both proofs of the same `DkgShareDecryption` circuit, so they share the + // same VK. Load it once and clone into both witness slots. + let c4_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::DkgShareDecryption, + )?; + let c4ab = C4abFoldWitness { + c4a_vk: c4_vk.verification_key.clone(), + c4a_proof: proof_field_strings(input.c4a_proof)?, + c4a_public: proof_public_field_strings(input.c4a_proof)?, + c4b_vk: c4_vk.verification_key.clone(), + c4b_proof: proof_field_strings(input.c4b_proof)?, + c4b_public: proof_public_field_strings(input.c4b_proof)?, + c4a_key_hash: c4_vk.key_hash.clone(), + c4b_key_hash: c4_vk.key_hash.clone(), + }; + let c4ab_proof = build_and_prove_recursive_bin( + prover, + CircuitName::C4abFold, + &c4ab, + &format!("{e3_id}-c4ab"), + artifacts_dir, + )?; + + let c0_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::PkBfv, + )?; + let c1_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::PkGeneration, + )?; + let c2ab_fold_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C2abFold, + )?; + let c3ab_fold_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C3abFold, + )?; + let c4ab_fold_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C4abFold, + )?; + + let nf = NodeFoldWitness { + c0_vk: c0_vk.verification_key, + c0_proof: proof_field_strings(input.c0_proof)?, + c0_public: proof_public_field_strings(input.c0_proof)?, + c1_vk: c1_vk.verification_key, + c1_proof: proof_field_strings(input.c1_proof)?, + c1_public: proof_public_field_strings(input.c1_proof)?, + c2ab_vk: c2ab_fold_vk.verification_key, + c2ab_proof: proof_field_strings(&c2ab_proof)?, + c2ab_public: proof_public_field_strings(&c2ab_proof)?, + c3ab_vk: c3ab_fold_vk.verification_key, + c3ab_proof: proof_field_strings(&c3ab_proof)?, + c3ab_public: proof_public_field_strings(&c3ab_proof)?, + c4ab_vk: c4ab_fold_vk.verification_key, + c4ab_proof: proof_field_strings(&c4ab_proof)?, + c4ab_public: proof_public_field_strings(&c4ab_proof)?, + party_id: u64_to_field_hex(input.party_id), + c0_key_hash: c0_vk.key_hash, + c1_key_hash: c1_vk.key_hash, + c2ab_key_hash: c2ab_fold_vk.key_hash, + c3ab_key_hash: c3ab_fold_vk.key_hash, + c4ab_key_hash: c4ab_fold_vk.key_hash, + }; + + build_and_prove_recursive_bin( + prover, + CircuitName::NodeFold, + &nf, + &format!("{e3_id}-nodefold"), + artifacts_dir, + ) +} + +/// Inputs for [`prove_dkg_aggregation`]. +pub struct DkgAggregationInput<'a> { + pub node_fold_proofs: &'a [Proof], + pub c5_proof: &'a Proof, + /// Honest party ids in the same order as `node_fold_proofs` (e.g. sorted ascending). + pub party_ids: &'a [u64], +} + +#[derive(Serialize)] +struct DkgAggregatorWitness { + nodes_fold_vk: Vec, + nodes_fold_proof: Vec, + nodes_fold_public: Vec, + c5_vk: Vec, + c5_proof: Vec, + c5_public: Vec, + nodes_fold_key_hash: String, + c5_key_hash: String, + party_ids: Vec, +} + +/// [`CircuitName::DkgAggregator`] over sequential [`CircuitName::NodesFold`] + C5, proved with +/// [`CircuitVariant::Evm`] for on-chain verification. +pub fn prove_dkg_aggregation( + prover: &ZkProver, + input: &DkgAggregationInput, + e3_id: &str, + artifacts_dir: &str, +) -> Result { + if input.node_fold_proofs.len() != input.party_ids.len() { + return Err(ZkError::InvalidInput( + "node_fold_proofs and party_ids length mismatch".into(), + )); + } + if input.node_fold_proofs.is_empty() { + return Err(ZkError::InvalidInput( + "prove_dkg_aggregation: need at least one NodeFold proof".into(), + )); + } + let h = input.node_fold_proofs.len(); + let slot_indices: Vec = (0u32..h as u32).collect(); + let nodes_fold_proof = generate_sequential_nodes_fold( + prover, + input.node_fold_proofs, + &slot_indices, + h, + &format!("{e3_id}-nodesfold"), + artifacts_dir, + )?; + + let nodes_fold_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::NodesFold, + )?; + let c5_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::PkAggregation, + )?; + + let party_id_fields: Vec = input + .party_ids + .iter() + .copied() + .map(u64_to_field_hex) + .collect(); + + let witness = DkgAggregatorWitness { + nodes_fold_vk: nodes_fold_vk.verification_key.clone(), + nodes_fold_proof: proof_field_strings(&nodes_fold_proof)?, + nodes_fold_public: proof_public_field_strings(&nodes_fold_proof)?, + c5_vk: c5_vk.verification_key.clone(), + c5_proof: proof_field_strings(input.c5_proof)?, + c5_public: proof_public_field_strings(input.c5_proof)?, + nodes_fold_key_hash: nodes_fold_vk.key_hash.clone(), + c5_key_hash: c5_vk.key_hash.clone(), + party_ids: party_id_fields, + }; + + let json = + serde_json::to_value(&witness).map_err(|e| ZkError::SerializationError(e.to_string()))?; + let input_map = inputs_json_to_input_map(&json)?; + let compiled = load_compiled_circuit( + prover, + CircuitName::DkgAggregator, + CircuitVariant::Default, + artifacts_dir, + )?; + let w = WitnessGenerator::new().generate_witness(&compiled, input_map)?; + prover.generate_proof_with_variant( + CircuitName::DkgAggregator, + &w, + e3_id, + CircuitVariant::Evm, + artifacts_dir, + ) +} + +/// One ciphertext index: C6 inners + C7 proof. +pub struct DecryptionAggregationJob<'a> { + pub c6_inner_proofs: &'a [Proof], + pub c6_slot_indices: &'a [u32], + pub c7_proof: &'a Proof, +} + +#[derive(Serialize)] +struct DecryptionAggregatorWitness { + c6_fold_vk: Vec, + c6_fold_proof: Vec, + c6_fold_public: Vec, + c7_vk: Vec, + c7_proof: Vec, + c7_public: Vec, + c6_fold_key_hash: String, + c7_key_hash: String, +} + +/// Prove [`CircuitName::DecryptionAggregator`] for each job (C6 fold + C7), with +/// [`CircuitVariant::Evm`] for on-chain verification. +pub fn prove_decryption_aggregation_jobs( + prover: &ZkProver, + c6_total_slots: usize, + jobs: &[DecryptionAggregationJob], + e3_id: &str, + artifacts_dir: &str, +) -> Result, ZkError> { + // VKs and the compiled circuit are job-independent: load once, reuse per ciphertext. + let c6_fold_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C6Fold, + )?; + let c7_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::DecryptedSharesAggregation, + )?; + let compiled = load_compiled_circuit( + prover, + CircuitName::DecryptionAggregator, + CircuitVariant::Default, + artifacts_dir, + )?; + + let mut out = Vec::with_capacity(jobs.len()); + for (i, job) in jobs.iter().enumerate() { + let c6_fold = generate_sequential_c6_fold( + prover, + job.c6_inner_proofs, + job.c6_slot_indices, + c6_total_slots, + &format!("{e3_id}-c6fold-{i}"), + artifacts_dir, + )?; + + let witness = DecryptionAggregatorWitness { + c6_fold_vk: c6_fold_vk.verification_key.clone(), + c6_fold_proof: proof_field_strings(&c6_fold)?, + c6_fold_public: proof_public_field_strings(&c6_fold)?, + c7_vk: c7_vk.verification_key.clone(), + c7_proof: proof_field_strings(job.c7_proof)?, + c7_public: proof_public_field_strings(job.c7_proof)?, + c6_fold_key_hash: c6_fold_vk.key_hash.clone(), + c7_key_hash: c7_vk.key_hash.clone(), + }; + + let json = serde_json::to_value(&witness) + .map_err(|e| ZkError::SerializationError(e.to_string()))?; + let input_map = inputs_json_to_input_map(&json)?; + let w = WitnessGenerator::new().generate_witness(&compiled, input_map)?; + let proof = prover.generate_proof_with_variant( + CircuitName::DecryptionAggregator, + &w, + &format!("{e3_id}-decagg-{i}"), + CircuitVariant::Evm, + artifacts_dir, + )?; + out.push(proof); + } + Ok(out) +} diff --git a/crates/zk-prover/src/circuits/aggregation/nodes_fold_accumulator.rs b/crates/zk-prover/src/circuits/aggregation/nodes_fold_accumulator.rs new file mode 100644 index 0000000000..5950579af4 --- /dev/null +++ b/crates/zk-prover/src/circuits/aggregation/nodes_fold_accumulator.rs @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Sequential [`CircuitName::NodesFold`]: each step verifies one inner [`CircuitName::NodeFold`] +//! proof and the previous accumulator (`nodes_fold` non-ZK proof). The first step proves +//! [`CircuitName::NodesFoldKernel`] at runtime to obtain a valid genesis `UltraHonkProof` (see +//! `circuits/bin/recursive_aggregation/nodes_fold_kernel`). + +use crate::circuits::aggregation::helpers::{ + parse_acc_public_field_strings_flat, sequential_fold, zero_field_hex_strings, + ACC_NONZK_PROOF_FIELDS, +}; +use crate::circuits::utils::{bytes_to_field_strings, inputs_json_to_input_map}; +use crate::circuits::vk; +use crate::error::ZkError; +use crate::prover::ZkProver; +use crate::witness::{CompiledCircuit, WitnessGenerator}; +use e3_events::{CircuitName, CircuitVariant, Proof}; +use serde::Serialize; + +/// Public-signal layout of `nodes_fold`: 4-field prefix, then `node_fold_fields`-wide tail. +const NODES_FOLD_PREFIX_LEN: usize = 4; + +fn node_fold_statement_field_count(proof: &Proof) -> Result { + if proof.circuit != CircuitName::NodeFold { + return Err(ZkError::InvalidInput(format!( + "expected NodeFold inner proof, got {}", + proof.circuit + ))); + } + let v = bytes_to_field_strings(proof.public_signals.as_ref())?; + if v.is_empty() { + return Err(ZkError::InvalidInput( + "NodeFold proof has empty public_signals".into(), + )); + } + Ok(v.len()) +} + +fn nodes_fold_acc_public_len(node_fold_fields: usize, total_slots: usize) -> usize { + 4 + total_slots * node_fold_fields +} + +#[derive(Serialize)] +struct NodesFoldStepInput { + inner_vk: Vec, + inner_proof: Vec, + node_fold_public_inputs: Vec, + acc_vk: Vec, + acc_proof: Vec, + acc_public_inputs: Vec, + inner_key_hash: String, + acc_key_hash: String, + is_first_step: bool, + slot_index: u32, +} + +/// Proves [`CircuitName::NodesFoldKernel`] for the same `inner` / `total_slots` / `slot_index` as the +/// fold step. +fn generate_nodes_fold_kernel_genesis_proof( + prover: &ZkProver, + inner: &Proof, + total_slots: usize, + slot_index: u32, + artifacts_dir: &str, + job_id: &str, +) -> Result { + let inner_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::NodeFold, + )?; + let kernel_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::NodesFoldKernel, + )?; + let nf_fields = node_fold_statement_field_count(inner)?; + let node_fold_public_inputs = bytes_to_field_strings(inner.public_signals.as_ref())?; + if node_fold_public_inputs.len() != nf_fields { + return Err(ZkError::InvalidInput( + "NodeFold public field length mismatch".into(), + )); + } + let expected_acc_pub = nodes_fold_acc_public_len(nf_fields, total_slots); + let acc_pi = zero_field_hex_strings(expected_acc_pub)?; + let acc_pf = zero_field_hex_strings(ACC_NONZK_PROOF_FIELDS)?; + + let full_input = NodesFoldStepInput { + inner_vk: inner_vk.verification_key, + inner_proof: bytes_to_field_strings(&inner.data)?, + node_fold_public_inputs, + acc_vk: kernel_vk.verification_key, + acc_proof: acc_pf, + acc_public_inputs: acc_pi, + inner_key_hash: inner_vk.key_hash, + acc_key_hash: kernel_vk.key_hash, + is_first_step: true, + slot_index, + }; + + let circuit_path = prover + .circuits_dir(CircuitVariant::Default, artifacts_dir) + .join(CircuitName::NodesFoldKernel.dir_path()) + .join(format!("{}.json", CircuitName::NodesFoldKernel.as_str())); + let compiled = CompiledCircuit::from_file(&circuit_path)?; + + let json = serde_json::to_value(&full_input) + .map_err(|e| ZkError::SerializationError(e.to_string()))?; + let input_map = inputs_json_to_input_map(&json)?; + let witness_gen = WitnessGenerator::new(); + let witness = witness_gen.generate_witness(&compiled, input_map)?; + + let proof = prover.generate_recursive_aggregation_bin_proof( + CircuitName::NodesFoldKernel, + &witness, + job_id, + artifacts_dir, + )?; + let _ = prover.cleanup(job_id); + Ok(proof) +} + +fn parse_nodes_fold_public_field_strings(proof: &Proof) -> Result, ZkError> { + // `nodes_fold` per-slot width is the inner `node_fold` statement length, which is only known at + // runtime; use the prefix-only parser and let the caller verify the total length against + // `nodes_fold_acc_public_len(node_fold_fields, total_slots)`. + parse_acc_public_field_strings_flat(proof, CircuitName::NodesFold, NODES_FOLD_PREFIX_LEN) +} + +fn generate_nodes_fold_step( + prover: &ZkProver, + inner: &Proof, + prior_fold: Option<&Proof>, + slot_index: u32, + total_slots: usize, + e3_id: &str, + artifacts_dir: &str, +) -> Result { + let is_first_step = prior_fold.is_none(); + + let inner_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::NodeFold, + )?; + let nf_fields = node_fold_statement_field_count(inner)?; + let node_fold_public_inputs = bytes_to_field_strings(inner.public_signals.as_ref())?; + if node_fold_public_inputs.len() != nf_fields { + return Err(ZkError::InvalidInput( + "NodeFold public field length mismatch".into(), + )); + } + + let expected_acc_pub = nodes_fold_acc_public_len(nf_fields, total_slots); + + let (acc_vk_art, acc_proof, acc_public_inputs) = if is_first_step { + let kernel_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::NodesFoldKernel, + )?; + let kernel_job_id = format!("{e3_id}-nodesfold-kernel"); + let kernel_proof = generate_nodes_fold_kernel_genesis_proof( + prover, + inner, + total_slots, + slot_index, + artifacts_dir, + &kernel_job_id, + )?; + let acc_pi = bytes_to_field_strings(kernel_proof.public_signals.as_ref())?; + if acc_pi.len() != expected_acc_pub { + return Err(ZkError::InvalidInput(format!( + "nodes_fold kernel proof public_inputs field count {} != expected {} (total_slots={})", + acc_pi.len(), + expected_acc_pub, + total_slots + ))); + } + ( + kernel_vk, + bytes_to_field_strings(&kernel_proof.data)?, + acc_pi, + ) + } else { + let p = prior_fold.expect("prior_fold required when is_first_step is false"); + let acc_pi = parse_nodes_fold_public_field_strings(p)?; + if acc_pi.len() != expected_acc_pub { + return Err(ZkError::InvalidInput(format!( + "prior nodes_fold public field count {} != expected {} (total_slots={})", + acc_pi.len(), + expected_acc_pub, + total_slots + ))); + } + ( + vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::NodesFold, + )?, + bytes_to_field_strings(&p.data)?, + acc_pi, + ) + }; + + let full_input = NodesFoldStepInput { + inner_vk: inner_vk.verification_key, + inner_proof: bytes_to_field_strings(&inner.data)?, + node_fold_public_inputs, + acc_vk: acc_vk_art.verification_key, + acc_proof, + acc_public_inputs, + inner_key_hash: inner_vk.key_hash, + acc_key_hash: acc_vk_art.key_hash, + is_first_step, + slot_index, + }; + + let circuit_path = prover + .circuits_dir(CircuitVariant::Default, artifacts_dir) + .join(CircuitName::NodesFold.dir_path()) + .join(format!("{}.json", CircuitName::NodesFold.as_str())); + let compiled = CompiledCircuit::from_file(&circuit_path)?; + + let json = serde_json::to_value(&full_input) + .map_err(|e| ZkError::SerializationError(e.to_string()))?; + let input_map = inputs_json_to_input_map(&json)?; + + let witness_gen = WitnessGenerator::new(); + let witness = witness_gen.generate_witness(&compiled, input_map)?; + + prover.generate_recursive_aggregation_bin_proof( + CircuitName::NodesFold, + &witness, + e3_id, + artifacts_dir, + ) +} + +/// Folds `inner_proofs` (one [`CircuitName::NodeFold`] per honest party) into a single +/// [`CircuitName::NodesFold`] proof for [`CircuitName::DkgAggregator`]. +/// +/// `slot_indices[i]` is the honest-slot index for `inner_proofs[i]` (must be `< total_slots`). +/// `total_slots` is `H` (honest committee size). +pub fn generate_sequential_nodes_fold( + prover: &ZkProver, + inner_proofs: &[Proof], + slot_indices: &[u32], + total_slots: usize, + e3_id: &str, + artifacts_dir: &str, +) -> Result { + sequential_fold( + "generate_sequential_nodes_fold", + inner_proofs, + slot_indices, + |inner, prior, slot| { + generate_nodes_fold_step( + prover, + inner, + prior, + slot, + total_slots, + e3_id, + artifacts_dir, + ) + }, + ) +} diff --git a/crates/zk-prover/src/circuits/mod.rs b/crates/zk-prover/src/circuits/mod.rs index a8878a7166..507ea99f9d 100644 --- a/crates/zk-prover/src/circuits/mod.rs +++ b/crates/zk-prover/src/circuits/mod.rs @@ -4,8 +4,8 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +pub mod aggregation; pub(crate) mod dkg; -pub mod recursive_aggregation; mod threshold; pub(crate) mod utils; pub(crate) mod vk; diff --git a/crates/zk-prover/src/circuits/recursive_aggregation/mod.rs b/crates/zk-prover/src/circuits/recursive_aggregation/mod.rs deleted file mode 100644 index 3843a48a02..0000000000 --- a/crates/zk-prover/src/circuits/recursive_aggregation/mod.rs +++ /dev/null @@ -1,594 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -//! Generic proof aggregation for recursive circuits. -//! -//! Contains the wrapper circuit (verifies 1-2 inner proofs) and the fold circuit -//! (folds two wrapper proofs). Share-computation-specific logic lives in -//! `circuits::dkg::share_computation`. - -use crate::circuits::utils::{bytes_to_field_strings, inputs_json_to_input_map}; -use crate::circuits::vk; -use crate::error::ZkError; -use crate::prover::ZkProver; -use crate::witness::{CompiledCircuit, WitnessGenerator}; -use e3_events::{CircuitName, CircuitVariant, Proof}; - -/// Full input for the recursive wrapper circuit. -struct WrapperInput { - verification_key: Vec, - proofs: Vec>, - public_inputs: Vec>, - key_hash: String, -} - -impl WrapperInput { - fn to_json(&self) -> Result { - serde_json::to_value(self).map_err(|e| ZkError::SerializationError(e.to_string())) - } -} - -impl serde::Serialize for WrapperInput { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - use serde::ser::SerializeMap; - let mut map = serializer.serialize_map(Some(4))?; - map.serialize_entry("verification_key", &self.verification_key)?; - map.serialize_entry("proofs", &self.proofs)?; - map.serialize_entry("public_inputs", &self.public_inputs)?; - map.serialize_entry("key_hash", &self.key_hash)?; - map.end() - } -} - -/// Generates a wrapper proof by executing the recursive wrapper circuit. -/// Loads verification_key and key_hash from the inner circuit (via `{circuit}.vk` and `{circuit}.vk_hash`). -/// -/// # Arguments -/// * `prover` - ZK prover with bb and circuits configured -/// * `proof` - Single proof to wrap (each wrapper circuit accepts exactly 1 proof) -/// * `e3_id` - Job identifier for work dir -/// -/// # Notes -/// The wrapper circuit lives at `recursive_aggregation/wrapper/{group}/{name}`. -/// Requires `{circuit}.vk` and `{circuit}.vk_hash` in the inner circuit dir (generated by build script). -pub fn generate_wrapper_proof( - prover: &ZkProver, - proof: &Proof, - e3_id: &str, - artifacts_dir: &str, -) -> Result { - let inner_circuit = proof.circuit; - let wrapper_circuit = inner_circuit.wrapper_artifact_circuit(); - - let proof_fields = vec![bytes_to_field_strings(&proof.data)?]; - let public_inputs = vec![bytes_to_field_strings(&proof.public_signals)?]; - - let vk_artifacts = vk::load_vk_artifacts( - &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), - inner_circuit, - )?; - - let full_input = WrapperInput { - verification_key: vk_artifacts.verification_key, - proofs: proof_fields, - public_inputs, - key_hash: vk_artifacts.key_hash, - }; - - let dir_path = wrapper_circuit.wrapper_dir_path(); - let circuit_path = prover - .circuits_dir(CircuitVariant::Default, artifacts_dir) - .join(&dir_path) - .join(format!("{}.json", wrapper_circuit.as_str())); - let compiled = CompiledCircuit::from_file(&circuit_path)?; - - let json = full_input.to_json()?; - let input_map = inputs_json_to_input_map(&json)?; - - let witness_gen = WitnessGenerator::new(); - let witness = witness_gen.generate_witness(&compiled, input_map)?; - - prover.generate_wrapper_proof(wrapper_circuit, &witness, e3_id, artifacts_dir) -} - -/// Full input for the fold circuit (recursive_aggregation/fold). -/// Generic names for two proofs, each with its own VK. -struct FoldInput { - proof1_verification_key: Vec, - proof1_proof: Vec, - proof1_public_inputs: Vec, - proof1_key_hash: String, - proof2_verification_key: Vec, - proof2_proof: Vec, - proof2_public_inputs: Vec, - proof2_key_hash: String, -} - -impl FoldInput { - fn to_json(&self) -> Result { - serde_json::to_value(self).map_err(|e| ZkError::SerializationError(e.to_string())) - } -} - -impl serde::Serialize for FoldInput { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - use serde::ser::SerializeMap; - let mut map = serializer.serialize_map(Some(8))?; - map.serialize_entry("proof1_verification_key", &self.proof1_verification_key)?; - map.serialize_entry("proof1_proof", &self.proof1_proof)?; - map.serialize_entry("proof1_public_inputs", &self.proof1_public_inputs)?; - map.serialize_entry("proof1_key_hash", &self.proof1_key_hash)?; - map.serialize_entry("proof2_verification_key", &self.proof2_verification_key)?; - map.serialize_entry("proof2_proof", &self.proof2_proof)?; - map.serialize_entry("proof2_public_inputs", &self.proof2_public_inputs)?; - map.serialize_entry("proof2_key_hash", &self.proof2_key_hash)?; - map.end() - } -} - -/// Generates the fold proof by folding two proofs. -/// VK path is chosen by circuit type: Fold uses dir_path, wrappers use wrapper_dir_path. -/// When `target_evm` is true, the proof is generated for on-chain EVM verification. -pub fn generate_fold_proof( - prover: &ZkProver, - proof1: &Proof, - proof2: &Proof, - e3_id: &str, - target_evm: bool, - artifacts_dir: &str, -) -> Result { - let vk1 = vk::load_vk_for_fold_input( - &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), - proof1.circuit, - )?; - let vk2 = vk::load_vk_for_fold_input( - &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), - proof2.circuit, - )?; - - // Both wrapper and fold output [key_hash, commitment]. - let proof1_public_inputs = bytes_to_field_strings(&proof1.public_signals)?; - let proof2_public_inputs = bytes_to_field_strings(&proof2.public_signals)?; - - if proof1_public_inputs.len() != 2 { - return Err(ZkError::InvalidInput(format!( - "proof1 must have exactly 2 public inputs, got {}", - proof1_public_inputs.len() - ))); - } - if proof2_public_inputs.len() != 2 { - return Err(ZkError::InvalidInput(format!( - "proof2 must have exactly 2 public inputs, got {}", - proof2_public_inputs.len() - ))); - } - - let full_input = FoldInput { - proof1_verification_key: vk1.verification_key, - proof1_proof: bytes_to_field_strings(&proof1.data)?, - proof1_public_inputs, - proof1_key_hash: vk1.key_hash, - proof2_verification_key: vk2.verification_key, - proof2_proof: bytes_to_field_strings(&proof2.data)?, - proof2_public_inputs, - proof2_key_hash: vk2.key_hash, - }; - - let variant = if target_evm { - CircuitVariant::Evm - } else { - CircuitVariant::Default - }; - let dir_path = CircuitName::Fold.dir_path(); - let circuit_path = prover - .circuits_dir(variant, artifacts_dir) - .join(&dir_path) - .join(format!("{}.json", CircuitName::Fold.as_str())); - let compiled = CompiledCircuit::from_file(&circuit_path)?; - - let json = full_input.to_json()?; - let input_map = inputs_json_to_input_map(&json)?; - - let witness_gen = WitnessGenerator::new(); - let witness = witness_gen.generate_witness(&compiled, input_map)?; - - if target_evm { - prover.generate_final_fold_proof(&witness, e3_id, artifacts_dir) - } else { - prover.generate_fold_proof(&witness, e3_id, artifacts_dir) - } -} - -#[cfg(all(test, feature = "integration-tests"))] -mod tests { - use super::{generate_fold_proof, generate_wrapper_proof}; - use crate::prover::ZkProver; - use crate::test_utils::get_tempdir; - use crate::traits::Provable; - use e3_config::BBPath; - use e3_fhe_params::BfvPreset; - use e3_zk_helpers::circuits::dkg::pk::circuit::{PkCircuit, PkCircuitData}; - use e3_zk_helpers::circuits::dkg::share_decryption::{ - ShareDecryptionCircuit, ShareDecryptionCircuitData, - }; - use e3_zk_helpers::computation::DkgInputType; - use e3_zk_helpers::dkg::share_encryption::{ - ShareEncryptionCircuit, ShareEncryptionCircuitData, - }; - use e3_zk_helpers::CiphernodesCommitteeSize; - use std::env; - use std::path::PathBuf; - - fn test_backend(temp_path: &std::path::Path) -> crate::backend::ZkBackend { - let noir_dir = temp_path.join("noir"); - let bb_binary = match env::var("E3_CUSTOM_BB") { - Ok(path) => BBPath::Custom(PathBuf::from(path)), - Err(_) => BBPath::Default(noir_dir.join("bin").join("bb")), - }; - let circuits_dir = { - let dist = dist_circuits_path(); - let version_file = dist.parent().map(|p| p.join("version.json")); - if dist.exists() && version_file.as_ref().is_some_and(|f| f.exists()) { - dist - } else { - noir_dir.join("circuits") - } - }; - let work_dir = noir_dir.join("work").join("test_node"); - crate::backend::ZkBackend::with_config( - bb_binary, - circuits_dir, - work_dir, - crate::config::ZkConfig::default(), - ) - } - - fn dist_circuits_path() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("dist") - .join("circuits") - } - - #[tokio::test] - async fn test_generate_and_verify_wrapper_proof() { - let temp = get_tempdir().unwrap(); - let mut backend = test_backend(temp.path()); - - backend.ensure_installed().await.expect("ensure_installed"); - - let dist = dist_circuits_path(); - let wrapper_src = dist - .join("default") - .join("recursive_aggregation") - .join("wrapper") - .join("dkg") - .join("pk"); - if wrapper_src.join("pk.json").exists() && wrapper_src.join("pk.vk").exists() { - // Use dist entirely so inner + wrapper circuits match (same build). - backend.circuits_dir = dist.clone(); - } - - let prover = ZkProver::new(&backend); - - let wrapper_dir = backend - .circuits_dir - .join("default") - .join("recursive_aggregation") - .join("wrapper") - .join("dkg") - .join("pk"); - if !wrapper_dir.join("pk.json").exists() || !wrapper_dir.join("pk.vk").exists() { - panic!( - "wrapper circuit not found at {} — run pnpm build:circuits and set circuits_dir to dist/circuits, or ensure the release includes recursive_aggregation", - wrapper_dir.display() - ); - } - - let preset = BfvPreset::InsecureThreshold512; - let artifacts_dir = preset.artifacts_dir(); - let sample = - PkCircuitData::generate_sample(preset).expect("sample data generation should succeed"); - - let e3_id = "aggregation-test-wrapper"; - - // First generate the inner proof with prove() (Recursive Variant) - let inner_proof = PkCircuit - .prove( - &prover, - &preset, - &sample, - &format!("{e3_id}_inner_0"), - &artifacts_dir, - ) - .expect("inner prove() should succeed"); - - let start = std::time::Instant::now(); - let wrapper_proof = generate_wrapper_proof(&prover, &inner_proof, e3_id, &artifacts_dir) - .expect("wrapper (1 proof)"); - let elapsed = start.elapsed(); - eprintln!("1-proof wrapper generation: {:?}", elapsed); - - assert!(!wrapper_proof.data.is_empty()); - assert!(!wrapper_proof.public_signals.is_empty()); - - let verified = prover - .verify_wrapper_proof(&wrapper_proof, e3_id, 0, &artifacts_dir) - .expect("verification should not error"); - assert!(verified, "wrapper proof should verify successfully"); - - prover.cleanup(&format!("{}_inner_0", e3_id)).unwrap(); - prover.cleanup(e3_id).unwrap(); - } - - #[tokio::test] - async fn test_generate_and_verify_wrapper_proof_2_proofs() { - let temp = get_tempdir().unwrap(); - let mut backend = test_backend(temp.path()); - - backend.ensure_installed().await.expect("ensure_installed"); - - let dist = dist_circuits_path(); - let wrapper_src = dist - .join("default") - .join("recursive_aggregation") - .join("wrapper") - .join("dkg") - .join("share_decryption"); - if wrapper_src.join("share_decryption.json").exists() - && wrapper_src.join("share_decryption.vk").exists() - { - backend.circuits_dir = dist.clone(); - } - - let prover = ZkProver::new(&backend); - - let wrapper_dir = backend - .circuits_dir - .join("default") - .join("recursive_aggregation") - .join("wrapper") - .join("dkg") - .join("share_decryption"); - if !wrapper_dir.join("share_decryption.json").exists() - || !wrapper_dir.join("share_decryption.vk").exists() - { - panic!( - "2-proof wrapper circuit not found at {} — run pnpm build:circuits and set circuits_dir to dist/circuits", - wrapper_dir.display() - ); - } - - let preset = BfvPreset::InsecureThreshold512; - let artifacts_dir = preset.artifacts_dir(); - let committee = CiphernodesCommitteeSize::Micro.values(); - let sample_a = ShareDecryptionCircuitData::generate_sample( - preset, - committee.clone(), - DkgInputType::SecretKey, - ) - .expect("sample A generation should succeed"); - let sample_b = - ShareDecryptionCircuitData::generate_sample(preset, committee, DkgInputType::SecretKey) - .expect("sample B generation should succeed"); - - let e3_id = "aggregation-2proof-wrapper"; - - // First generate the inner proofs with prove() (Recursive Variant) - let inner_proof_a = ShareDecryptionCircuit - .prove( - &prover, - &preset, - &sample_a, - &format!("{e3_id}_inner_0"), - &artifacts_dir, - ) - .expect("inner prove() A should succeed"); - let inner_proof_b = ShareDecryptionCircuit - .prove( - &prover, - &preset, - &sample_b, - &format!("{e3_id}_inner_1"), - &artifacts_dir, - ) - .expect("inner prove() B should succeed"); - - let start = std::time::Instant::now(); - let wrapper_a = generate_wrapper_proof(&prover, &inner_proof_a, e3_id, &artifacts_dir) - .expect("wrapper (proof A)"); - let wrapper_b = generate_wrapper_proof(&prover, &inner_proof_b, e3_id, &artifacts_dir) - .expect("wrapper (proof B)"); - let fold_proof = generate_fold_proof( - &prover, - &wrapper_a, - &wrapper_b, - e3_id, - false, - &artifacts_dir, - ) - .expect("fold 2 wrappers"); - let elapsed = start.elapsed(); - eprintln!("2-proof (wrapper each + fold) generation: {:?}", elapsed); - - assert!(!fold_proof.data.is_empty()); - assert!(!fold_proof.public_signals.is_empty()); - - let verified = prover - .verify_fold_proof(&fold_proof, e3_id, 0, &artifacts_dir) - .expect("verification should not error"); - assert!(verified, "fold of 2 wrappers should verify successfully"); - - prover - .cleanup("aggregation-2proof-wrapper_inner_0") - .unwrap(); - prover - .cleanup("aggregation-2proof-wrapper_inner_1") - .unwrap(); - prover.cleanup(e3_id).unwrap(); - } - - #[tokio::test] - async fn test_generate_and_verify_fold_proof() { - let temp = get_tempdir().unwrap(); - let mut backend = test_backend(temp.path()); - - backend.ensure_installed().await.expect("ensure_installed"); - - let dist = dist_circuits_path(); - { - let default_dist = dist.join("default"); - if default_dist - .join("recursive_aggregation") - .join("wrapper") - .join("dkg") - .join("pk") - .join("pk.json") - .exists() - { - backend.circuits_dir = dist.clone(); - } - } - - let prover = ZkProver::new(&backend); - - let default_dir = backend.circuits_dir.join("default"); - let pk_wrapper = default_dir - .join("recursive_aggregation") - .join("wrapper") - .join("dkg") - .join("pk"); - let share_enc_wrapper = default_dir - .join("recursive_aggregation") - .join("wrapper") - .join("dkg") - .join("share_encryption"); - let fold_dir = default_dir.join("recursive_aggregation").join("fold"); - - if !pk_wrapper.join("pk.json").exists() - || !pk_wrapper.join("pk.vk").exists() - || !share_enc_wrapper.join("share_encryption.json").exists() - || !share_enc_wrapper.join("share_encryption.vk").exists() - { - panic!( - "wrapper circuits not found — run pnpm build:circuits and ensure dist/circuits includes recursive_aggregation wrappers for pk and share_encryption", - ); - } - if !fold_dir.join("fold.json").exists() || !fold_dir.join("fold.vk").exists() { - panic!( - "fold circuit not found at {} — run pnpm build:circuits", - fold_dir.display() - ); - } - - let preset = BfvPreset::InsecureThreshold512; - let artifacts_dir = preset.artifacts_dir(); - let committee = CiphernodesCommitteeSize::Micro.values(); - let sd = preset.search_defaults().expect("search_defaults"); - - let pk_sample = - PkCircuitData::generate_sample(preset).expect("pk sample generation should succeed"); - let share_enc_sample_secret = ShareEncryptionCircuitData::generate_sample( - preset, - committee.clone(), - DkgInputType::SecretKey, - sd.z, - sd.lambda, - ) - .expect("share_encryption sample (secret) generation should succeed"); - - let share_enc_sample_noise = ShareEncryptionCircuitData::generate_sample( - preset, - committee, - DkgInputType::SmudgingNoise, - sd.z, - sd.lambda, - ) - .expect("share_encryption sample (noise) generation should succeed"); - - let e3_id = "aggregation-test-fold"; - - // Generate inner proofs first with prove() (Recursive Variant) - let pk_inner_proof = PkCircuit - .prove( - &prover, - &preset, - &pk_sample, - &format!("{e3_id}_pk_inner_0"), - &artifacts_dir, - ) - .expect("pk inner prove() should succeed"); - - let pk_wrapper_proof = - generate_wrapper_proof(&prover, &pk_inner_proof, e3_id, &artifacts_dir) - .expect("pk wrapper"); - - let enc_inner_secret = ShareEncryptionCircuit - .prove( - &prover, - &preset, - &share_enc_sample_secret, - &format!("{e3_id}_enc_inner_0"), - &artifacts_dir, - ) - .expect("share_encryption inner prove() (secret) should succeed"); - let enc_inner_noise = ShareEncryptionCircuit - .prove( - &prover, - &preset, - &share_enc_sample_noise, - &format!("{e3_id}_enc_inner_1"), - &artifacts_dir, - ) - .expect("share_encryption inner prove() (noise) should succeed"); - - let share_enc_wrapper_1 = - generate_wrapper_proof(&prover, &enc_inner_secret, e3_id, &artifacts_dir) - .expect("share_encryption wrapper (secret)"); - let share_enc_wrapper_2 = - generate_wrapper_proof(&prover, &enc_inner_noise, e3_id, &artifacts_dir) - .expect("share_encryption wrapper (noise)"); - let share_enc_fold_proof = generate_fold_proof( - &prover, - &share_enc_wrapper_1, - &share_enc_wrapper_2, - e3_id, - false, - &artifacts_dir, - ) - .expect("fold share_enc wrappers"); - let fold_proof = generate_fold_proof( - &prover, - &share_enc_fold_proof, - &pk_wrapper_proof, - e3_id, - false, - &artifacts_dir, - ) - .expect("fold"); - - assert!(!fold_proof.data.is_empty()); - assert!(!fold_proof.public_signals.is_empty()); - assert_eq!(fold_proof.circuit, e3_events::CircuitName::Fold); - - let verified = prover - .verify_fold_proof(&fold_proof, e3_id, 0, &artifacts_dir) - .expect("verification should not error"); - assert!(verified, "fold proof should verify successfully"); - - prover.cleanup(&format!("{}_pk_inner_0", e3_id)).unwrap(); - prover.cleanup(&format!("{}_enc_inner_0", e3_id)).unwrap(); - prover.cleanup(&format!("{}_enc_inner_1", e3_id)).unwrap(); - prover.cleanup(e3_id).unwrap(); - } -} diff --git a/crates/zk-prover/src/circuits/utils.rs b/crates/zk-prover/src/circuits/utils.rs index 8c24180621..7499b2def4 100644 --- a/crates/zk-prover/src/circuits/utils.rs +++ b/crates/zk-prover/src/circuits/utils.rs @@ -15,6 +15,33 @@ use noirc_abi::{input_parser::InputValue, InputMap}; const FIELD_SIZE: usize = 32; +/// Matches `bb_proof_verification::RECURSIVE_ZK_PROOF_LENGTH` (`UltraHonkZKProof`). +pub const ULTRA_HONK_ZK_PROOF_FIELD_COUNT: usize = 508; + +/// Converts raw ZK proof bytes to field strings for `UltraHonkZKProof` witness input. +/// +/// Barretenberg often emits fewer than [`ULTRA_HONK_ZK_PROOF_FIELD_COUNT`] 32-byte limbs; the Noir +/// type is a fixed-width array, so shorter proofs are zero-padded at the end. +pub fn zk_proof_bytes_to_field_strings(bytes: &[u8]) -> Result, ZkError> { + let need = ULTRA_HONK_ZK_PROOF_FIELD_COUNT * FIELD_SIZE; + if bytes.len() > need { + return Err(ZkError::InvalidInput(format!( + "zk proof bytes length {} exceeds fixed width {}", + bytes.len(), + need + ))); + } + if bytes.len() % FIELD_SIZE != 0 { + return Err(ZkError::InvalidInput(format!( + "zk proof bytes length must be multiple of {FIELD_SIZE}, got {}", + bytes.len() + ))); + } + let mut buf = bytes.to_vec(); + buf.resize(need, 0); + bytes_to_field_strings(&buf) +} + /// Converts raw proof/public-signal bytes (32-byte big-endian chunks) to hex-encoded field strings. pub fn bytes_to_field_strings(bytes: &[u8]) -> Result, ZkError> { if bytes.len() % FIELD_SIZE != 0 { @@ -92,6 +119,12 @@ fn json_value_to_input_value(v: &serde_json::Value) -> Result Result Result { - let circuit_dir = circuits_dir.join(circuit.wrapper_dir_path()); - load_vk_from_dir(&circuit_dir, circuit.as_str()) -} - /// Loads VK artifacts from `.vk` and `.vk_hash` in the variant-specific circuits directory. /// The caller is responsible for passing the correct circuits_dir: /// - `circuits_dir(CircuitVariant::Recursive)` for inner/base proofs embedded in a wrapper @@ -67,14 +57,10 @@ pub fn load_vk_artifacts( load_vk_from_dir(&circuit_dir, circuit.as_str()) } -/// VK path by circuit type: Fold uses dir_path, wrappers use wrapper_dir_path. +/// Loads VK artifacts for a proof being combined into a recursive aggregation circuit. pub fn load_vk_for_fold_input( circuits_dir: &Path, circuit: CircuitName, ) -> Result { - if circuit == CircuitName::Fold { - load_vk_artifacts(circuits_dir, circuit) - } else { - load_wrapper_vk_artifacts(circuits_dir, circuit) - } + load_vk_artifacts(circuits_dir, circuit) } diff --git a/crates/zk-prover/src/lib.rs b/crates/zk-prover/src/lib.rs index 011a4e2873..98ee6344a3 100644 --- a/crates/zk-prover/src/lib.rs +++ b/crates/zk-prover/src/lib.rs @@ -21,7 +21,13 @@ pub use actors::{ }; pub use backend::{SetupStatus, ZkBackend}; -pub use circuits::recursive_aggregation::{generate_fold_proof, generate_wrapper_proof}; +pub use circuits::aggregation::c3_accumulator::generate_sequential_c3_fold; +pub use circuits::aggregation::c6_accumulator::generate_sequential_c6_fold; +pub use circuits::aggregation::node_dkg_fold::{ + prove_decryption_aggregation_jobs, prove_dkg_aggregation, prove_node_dkg_fold, + DecryptionAggregationJob, DkgAggregationInput, NodeDkgFoldInput, +}; +pub use circuits::aggregation::nodes_fold_accumulator::generate_sequential_nodes_fold; pub use config::{verify_checksum, BbTarget, CircuitInfo, VersionInfo, ZkConfig}; pub use e3_events::CircuitVariant; pub use e3_zk_helpers::circuits::dkg::pk::circuit::PkCircuit; diff --git a/crates/zk-prover/src/prover.rs b/crates/zk-prover/src/prover.rs index 98f1ecb5db..262e8fe027 100644 --- a/crates/zk-prover/src/prover.rs +++ b/crates/zk-prover/src/prover.rs @@ -90,8 +90,8 @@ impl ZkProver { ) } - /// Wrapper proof (Default variant, wrapper dir). - pub fn generate_wrapper_proof( + /// Proof for a `recursive_aggregation/*` bin circuit (Default / `noir-recursive-no-zk`). + pub fn generate_recursive_aggregation_bin_proof( &self, circuit: CircuitName, witness_data: &[u8], @@ -102,48 +102,12 @@ impl ZkProver { circuit, witness_data, e3_id, - &circuit.wrapper_dir_path(), - CircuitVariant::Default, - artifacts_dir, - ) - } - - /// Fold proof (Default variant). - pub fn generate_fold_proof( - &self, - witness_data: &[u8], - e3_id: &str, - artifacts_dir: &str, - ) -> Result { - let dir = CircuitName::Fold.dir_path(); - self.generate_proof_impl( - CircuitName::Fold, - witness_data, - e3_id, - &dir, + &circuit.dir_path(), CircuitVariant::Default, artifacts_dir, ) } - /// Final fold proof for on-chain verification (Evm variant). - pub fn generate_final_fold_proof( - &self, - witness_data: &[u8], - e3_id: &str, - artifacts_dir: &str, - ) -> Result { - let dir = CircuitName::Fold.dir_path(); - self.generate_proof_impl( - CircuitName::Fold, - witness_data, - e3_id, - &dir, - CircuitVariant::Evm, - artifacts_dir, - ) - } - fn generate_proof_impl( &self, circuit: CircuitName, @@ -315,27 +279,7 @@ impl ZkProver { self.verify_proof_with_variant(proof, e3_id, party_id, CircuitVariant::Evm, artifacts_dir) } - /// Verifies a wrapper proof (Default Variant, wrapper dir). - pub fn verify_wrapper_proof( - &self, - proof: &Proof, - e3_id: &str, - party_id: u64, - artifacts_dir: &str, - ) -> Result { - self.verify_proof_impl( - proof.circuit, - &proof.data, - &proof.public_signals, - proof.circuit.wrapper_dir_path(), - e3_id, - party_id, - CircuitVariant::Default, - artifacts_dir, - ) - } - - /// Verifies a fold proof (Default variant). + /// Verifies a recursive-aggregation bin proof (Default variant). pub fn verify_fold_proof( &self, proof: &Proof, @@ -343,18 +287,8 @@ impl ZkProver { party_id: u64, artifacts_dir: &str, ) -> Result { - use e3_events::CircuitName; - if proof.circuit != CircuitName::Fold { - return Err(ZkError::InvalidInput(format!( - "expected Fold proof, got {}", - proof.circuit - ))); - } - self.verify_proof_impl( - proof.circuit, - &proof.data, - &proof.public_signals, - proof.circuit.dir_path(), + self.verify_proof_with_variant( + proof, e3_id, party_id, CircuitVariant::Default, diff --git a/crates/zk-prover/src/test_utils.rs b/crates/zk-prover/src/test_utils.rs index dd3d9b14b0..586d142dc6 100644 --- a/crates/zk-prover/src/test_utils.rs +++ b/crates/zk-prover/src/test_utils.rs @@ -7,8 +7,24 @@ use std::{fs, path::Path}; use anyhow::Result; +use noirc_abi::InputMap; +use serde_json::Value; use tempfile::TempDir; +use crate::error::ZkError; + +pub use crate::circuits::vk::load_vk_artifacts; + +/// Field strings for recursive aggregation witness I/O (integration tests only). +pub fn fold_witness_field_strings(bytes: &[u8]) -> Result, ZkError> { + crate::circuits::utils::bytes_to_field_strings(bytes) +} + +/// JSON → Noir input map for fold witness generation (integration tests only). +pub fn fold_witness_input_map(json: &Value) -> Result { + crate::circuits::utils::inputs_json_to_input_map(json) +} + /// Get the tempdir within ./target/tmp. This is important since some virtual environments such as nix /// won't necessarily have access to bb globaly. Not all tmp operations need to use this path only /// operations that require tools to exist within a shell at that location. diff --git a/crates/zk-prover/tests/common/helpers.rs b/crates/zk-prover/tests/common/helpers.rs index 3fb0223031..3400842efc 100644 --- a/crates/zk-prover/tests/common/helpers.rs +++ b/crates/zk-prover/tests/common/helpers.rs @@ -5,6 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use e3_config::BBPath; +use e3_events::CircuitName; pub use e3_test_helpers::{find_anvil, find_bb}; use e3_zk_prover::{ZkBackend, ZkConfig}; use std::{env, path::PathBuf}; @@ -122,6 +123,70 @@ pub async fn setup_compiled_circuit(backend: &ZkBackend, group: &str, circuit_na } } +/// Stages a `recursive_aggregation/*` fold binary for [`CircuitVariant::Default`] (`noir-recursive-no-zk`). +/// +/// `pnpm build:circuits` writes `{package}.vk_recursive` (+ `_hash`) under `circuits/bin/recursive_aggregation//target/`. +/// [`CircuitName::DkgAggregator`] also gets `{package}.vk` / `.vk_hash` (`bb write_vk -t evm`); when present, they are +/// copied into `insecure-512/evm/...` for [`CircuitVariant::Evm`] proving. +pub async fn setup_recursive_aggregation_fold_circuit(backend: &ZkBackend, circuit: CircuitName) { + let pkg = circuit.as_str(); + let target_dir = circuits_build_root() + .join("recursive_aggregation") + .join(pkg) + .join("target"); + + let json_path = target_dir.join(format!("{pkg}.json")); + let vk_recursive_path = target_dir.join(format!("{pkg}.vk_recursive")); + let vk_recursive_hash_path = target_dir.join(format!("{pkg}.vk_recursive_hash")); + let vk_evm_path = target_dir.join(format!("{pkg}.vk")); + let vk_evm_hash_path = target_dir.join(format!("{pkg}.vk_hash")); + + assert!( + json_path.exists(), + "compiled fold circuit JSON not found: {} (run `pnpm build:circuits --group recursive_aggregation`)", + json_path.display() + ); + assert!( + vk_recursive_path.exists(), + "noir-recursive-no-zk VK not found: {} (aggregation circuits only emit `.vk_recursive`; run `pnpm build:circuits`)", + vk_recursive_path.display() + ); + + let preset_dir = backend.circuits_dir.join("insecure-512"); + let default_dir = preset_dir.join("default").join(circuit.group()).join(pkg); + fs::create_dir_all(&default_dir).await.unwrap(); + fs::copy(&json_path, default_dir.join(format!("{pkg}.json"))) + .await + .unwrap(); + fs::copy(&vk_recursive_path, default_dir.join(format!("{pkg}.vk"))) + .await + .unwrap(); + if vk_recursive_hash_path.exists() { + fs::copy( + &vk_recursive_hash_path, + default_dir.join(format!("{pkg}.vk_hash")), + ) + .await + .unwrap(); + } + + if vk_evm_path.exists() { + let evm_dir = preset_dir.join("evm").join(circuit.group()).join(pkg); + fs::create_dir_all(&evm_dir).await.unwrap(); + fs::copy(&json_path, evm_dir.join(format!("{pkg}.json"))) + .await + .unwrap(); + fs::copy(&vk_evm_path, evm_dir.join(format!("{pkg}.vk"))) + .await + .unwrap(); + if vk_evm_hash_path.exists() { + fs::copy(&vk_evm_hash_path, evm_dir.join(format!("{pkg}.vk_hash"))) + .await + .unwrap(); + } + } +} + /// Creates a temp ZkBackend with the real bb binary symlinked in. /// Caller must hold onto the returned TempDir or it gets cleaned up. pub async fn setup_test_prover(bb: &PathBuf) -> (ZkBackend, TempDir) { diff --git a/crates/zk-prover/tests/common/node_fold_witness.rs b/crates/zk-prover/tests/common/node_fold_witness.rs new file mode 100644 index 0000000000..2fbad89564 --- /dev/null +++ b/crates/zk-prover/tests/common/node_fold_witness.rs @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Correlated DKG witness pieces for `node_fold`: [`PkGenerationCircuitData`] plus +//! [`ShareComputationCircuitData`] built from the same secrets so C1 ↔ C2 commitments align. + +use e3_fhe_params::build_pair_for_preset; +use e3_fhe_params::create_deterministic_crp_from_default_seed; +use e3_fhe_params::BfvPreset; +use e3_polynomial::CrtPolynomial; +use e3_zk_helpers::circuits::dkg::share_computation::utils::compute_parity_matrix; +use e3_zk_helpers::computation::DkgInputType; +use e3_zk_helpers::dkg::share_computation::{ + Inputs as ShareComputationInputs, ShareComputationCircuitData, +}; +use e3_zk_helpers::dkg::share_encryption::ShareEncryptionCircuitData; +use e3_zk_helpers::threshold::pk_generation::PkGenerationCircuitData; +use e3_zk_helpers::CiphernodesCommittee; +use e3_zk_helpers::CircuitsErrors; +use fhe::bfv::Encoding; +use fhe::bfv::SecretKey; +use fhe::mbfv::PublicKeyShare; +use fhe::trbfv::{ShareManager, TRBFV}; +use fhe::{bfv::Plaintext, bfv::PublicKey}; +use fhe_traits::FheEncoder; +use ndarray::Array2; +use num_bigint::BigInt; +use num_traits::{Signed, ToPrimitive}; +use rand::thread_rng; +use std::ops::Deref; + +/// Same as [`PkGenerationCircuitData::generate_sample`], plus smudging coefficients for C2b. +/// +/// Returns the [`SecretKey`] used for [`PublicKeyShare::new_extended`] so correlated C2a shares can +/// be built with [`ShareComputationCircuitData::generate_sample`]-compatible +/// `coeffs_to_poly_level0(secret_key.coeffs)` (not a round-trip through `pk.sk` limb 0). +pub fn pk_generation_sample_with_esi( + preset: BfvPreset, + committee: CiphernodesCommittee, +) -> Result<(PkGenerationCircuitData, Vec, SecretKey), CircuitsErrors> { + let (threshold_params, _) = build_pair_for_preset(preset) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to build pair for preset: {:?}", e)))?; + + let mut rng = thread_rng(); + + let secret_key = SecretKey::random(&threshold_params, &mut rng); + let crp = create_deterministic_crp_from_default_seed(&threshold_params); + + let (pk0_share, _, _sk_fhe, e) = + PublicKeyShare::new_extended(&secret_key, crp.clone(), &mut rng).map_err(|e| { + CircuitsErrors::Sample(format!("Failed to create public key share: {:?}", e)) + })?; + + let sk_coeffs: Vec = secret_key.coeffs.iter().map(|&c| BigInt::from(c)).collect(); + let mut sk_crt = CrtPolynomial::from_mod_q_polynomial(&sk_coeffs, threshold_params.moduli()); + sk_crt + .center(threshold_params.moduli()) + .map_err(|e| CircuitsErrors::Sample(format!("center sk CRT: {:?}", e)))?; + + let num_parties = committee.n; + let threshold = committee.threshold; + let preset_metadata = preset.metadata(); + + let defaults = preset + .search_defaults() + .ok_or_else(|| CircuitsErrors::Sample("missing search defaults".to_string()))?; + let num_ciphertexts = defaults.z; + + let trbfv = TRBFV::new(num_parties, threshold, threshold_params.clone()) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to create TRBFV: {:?}", e)))?; + let share_manager = ShareManager::new(num_parties, threshold, threshold_params.clone()); + + let esi_coeffs: Vec = trbfv + .generate_smudging_error(num_ciphertexts as usize, preset_metadata.lambda, &mut rng) + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to generate smudging error: {:?}", e)) + })?; + + let e_sm_rns_zeroizing = share_manager + .bigints_to_poly(&esi_coeffs) + .map_err(|e| CircuitsErrors::Sample(format!("bigints_to_poly: {:?}", e)))?; + + let e_sm = e_sm_rns_zeroizing.deref().clone(); + + let pk = PkGenerationCircuitData { + committee, + pk0_share: CrtPolynomial::from_fhe_polynomial(&pk0_share), + eek: CrtPolynomial::from_fhe_polynomial(&e), + e_sm: CrtPolynomial::from_fhe_polynomial(&e_sm), + sk: sk_crt, + }; + + Ok((pk, esi_coeffs, secret_key)) +} + +/// C2a: same `sk` CRT as [`PkGenerationCircuitData::sk`]; Shamir shares from `secret_key.coeffs` +/// like [`ShareComputationCircuitData::generate_sample`] / `gen_pk_share_and_sk_sss`. +pub fn share_computation_sk_from_pk( + preset: BfvPreset, + committee: CiphernodesCommittee, + pk: &PkGenerationCircuitData, + secret_key: &SecretKey, +) -> Result { + let (threshold_params, _) = build_pair_for_preset(preset) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to build pair for preset: {:?}", e)))?; + let mut rng = thread_rng(); + + let parity_matrix = + compute_parity_matrix(threshold_params.moduli(), committee.n, committee.threshold) + .map_err(|e| CircuitsErrors::Sample(e))?; + + let mut share_manager = + ShareManager::new(committee.n, committee.threshold, threshold_params.clone()); + + let sk_poly = share_manager + .coeffs_to_poly_level0(secret_key.coeffs.clone().as_ref()) + .map_err(|e| CircuitsErrors::Sample(format!("coeffs_to_poly_level0: {:?}", e)))?; + let sk_sss_u64 = share_manager + .generate_secret_shares_from_poly(sk_poly, &mut rng) + .map_err(|e| { + CircuitsErrors::Sample(format!("generate_secret_shares_from_poly: {:?}", e)) + })?; + let secret_sss: Vec> = sk_sss_u64 + .into_iter() + .map(|a| a.mapv(BigInt::from)) + .collect(); + + Ok(ShareComputationCircuitData { + dkg_input_type: DkgInputType::SecretKey, + secret: pk.sk.clone(), + secret_sss, + parity_matrix, + n_parties: committee.n as u32, + threshold: committee.threshold as u32, + }) +} + +/// C2b: `esi_coeffs` from [`pk_generation_sample_with_esi`]; `secret` matches [`PkGenerationCircuitData::e_sm`]. +pub fn share_computation_esm_from_esi( + preset: BfvPreset, + committee: CiphernodesCommittee, + pk: &PkGenerationCircuitData, + esi_coeffs: &[BigInt], +) -> Result { + let (threshold_params, _) = build_pair_for_preset(preset) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to build pair for preset: {:?}", e)))?; + let mut rng = thread_rng(); + + let parity_matrix = + compute_parity_matrix(threshold_params.moduli(), committee.n, committee.threshold) + .map_err(|e| CircuitsErrors::Sample(e))?; + + let mut share_manager = + ShareManager::new(committee.n, committee.threshold, threshold_params.clone()); + + let esi_poly = share_manager + .bigints_to_poly(esi_coeffs) + .map_err(|e| CircuitsErrors::Sample(format!("bigints_to_poly: {:?}", e)))?; + let esi_sss_u64 = share_manager + .generate_secret_shares_from_poly(esi_poly, &mut rng) + .map_err(|e| { + CircuitsErrors::Sample(format!("generate_secret_shares_from_poly: {:?}", e)) + })?; + let secret_sss: Vec> = esi_sss_u64 + .into_iter() + .map(|a| a.mapv(BigInt::from)) + .collect(); + + Ok(ShareComputationCircuitData { + dkg_input_type: DkgInputType::SmudgingNoise, + secret: pk.e_sm.clone(), + secret_sss, + parity_matrix, + n_parties: committee.n as u32, + threshold: committee.threshold as u32, + }) +} + +/// One [`ShareEncryptionCircuitData`] for C3 slot index `slot`. +pub fn share_encryption_for_slot( + preset: BfvPreset, + dkg_sk: &SecretKey, + dkg_pk: &PublicKey, + share_inputs: &ShareComputationInputs, + slot: usize, + dkg_input_type: DkgInputType, +) -> Result { + let (_, dkg_params) = + build_pair_for_preset(preset).map_err(|e| CircuitsErrors::Sample(e.to_string()))?; + + let (threshold_params, _) = build_pair_for_preset(preset) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to build pair for preset: {:?}", e)))?; + let l = threshold_params.moduli().len(); + let party = slot / l; + let mod_ix = slot % l; + + let degree = share_inputs.y.len(); + let mut share_row = Vec::with_capacity(degree); + for coeff in 0..degree { + let v = share_inputs.y[coeff][mod_ix][1 + party].clone(); + let q = BigInt::from(threshold_params.moduli()[mod_ix]); + let mut x = v % &q; + if x.is_negative() { + x += &q; + } + let u = x.to_u64().ok_or_else(|| { + CircuitsErrors::Sample("share coefficient does not fit u64 for plaintext".into()) + })?; + share_row.push(u); + } + + let mut rng = thread_rng(); + let pt = Plaintext::try_encode(&share_row, Encoding::poly(), &dkg_params) + .map_err(|e| CircuitsErrors::Sample(format!("encode plaintext: {:?}", e)))?; + + let (_ct, u_rns, e0_rns, e1_rns) = dkg_pk + .try_encrypt_extended(&pt, &mut rng) + .map_err(|e| CircuitsErrors::Sample(format!("encrypt: {:?}", e)))?; + + Ok(ShareEncryptionCircuitData { + plaintext: pt, + ciphertext: _ct, + public_key: dkg_pk.clone(), + secret_key: dkg_sk.clone(), + u_rns, + e0_rns, + e1_rns, + dkg_input_type, + }) +} diff --git a/crates/zk-prover/tests/fold_accumulators_e2e_tests.rs b/crates/zk-prover/tests/fold_accumulators_e2e_tests.rs new file mode 100644 index 0000000000..e5f8d91025 --- /dev/null +++ b/crates/zk-prover/tests/fold_accumulators_e2e_tests.rs @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Fold **accumulators** integration tests: sequential [`generate_sequential_c3_fold`] / +//! [`generate_sequential_c6_fold`] (prove + [`ZkProver::verify_fold_proof`]), ABI/slot inference from +//! compiled `c3_fold` / `c6_fold` JSON, and artifact staging under [`CircuitVariant::Default`] +//! (`noir-recursive-no-zk` VKs — see `scripts/build-circuits.ts`). +//! +//! Loads compiled JSON for the node-fold **pipeline** ([`CircuitName::C2abFold`] … [`CircuitName::NodeFold`]) +//! and stages those artifacts; it does **not** run a full correlated `node_fold` proof — use +//! `node_fold_correlated_e2e_tests.rs` for that. +//! +//! - [`recursive_aggregation_default_artifacts_staged`]: staged `c3_fold` paths (no `bb prove`). +//! - [`recursive_aggregation_c6_fold_kernel_artifacts_staged`]: staged `c6_fold_kernel` paths. +//! - [`c3_fold_sequential_proves_and_verifies`]: two inner `ShareEncryption` proofs → [`generate_sequential_c3_fold`]. +//! - [`c6_fold_sequential_proves_and_verifies`]: two inner `ThresholdShareDecryption` proofs → [`generate_sequential_c6_fold`]. +//! - [`node_fold_pipeline_compiled_json_load`] / [`node_fold_pipeline_recursive_aggregation_artifacts_staged`]: +//! pipeline circuits load + staged artifacts for C2ab/C3ab/C4ab/NodeFold. + +mod common; + +use std::path::PathBuf; + +use common::{ + find_bb, setup_compiled_circuit, setup_recursive_aggregation_fold_circuit, setup_test_prover, +}; +use e3_events::CircuitName; +use e3_fhe_params::BfvPreset; +use e3_zk_helpers::computation::DkgInputType; +use e3_zk_helpers::dkg::share_encryption::{ShareEncryptionCircuit, ShareEncryptionCircuitData}; +use e3_zk_helpers::threshold::share_decryption::{ + ShareDecryptionCircuit, ShareDecryptionCircuitData, +}; +use e3_zk_helpers::CiphernodesCommitteeSize; +use e3_zk_prover::{ + generate_sequential_c3_fold, generate_sequential_c6_fold, CircuitVariant, CompiledCircuit, + Provable, ZkBackend, ZkProver, +}; + +fn c3_fold_json_path() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../circuits/bin/recursive_aggregation/c3_fold/target/c3_fold.json") +} + +fn c6_fold_json_path() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../circuits/bin/recursive_aggregation/c6_fold/target/c6_fold.json") +} + +fn recursive_aggregation_compiled_json_path(circuit: CircuitName) -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../circuits/bin") + .join(circuit.dir_path()) + .join("target") + .join(format!("{}.json", circuit.as_str())) +} + +/// `c2ab_fold` → `c3ab_fold` → `c4ab_fold` → inputs to `node_fold` (see `node_fold/src/main.nr`). +const NODE_FOLD_PIPELINE: &[CircuitName] = &[ + CircuitName::C2abFold, + CircuitName::C3abFold, + CircuitName::C4abFold, + CircuitName::NodeFold, +]; + +/// Reads `C3_SLOTS` from the compiled `c3_fold` ABI (`acc_public_inputs` length is `4 + 3 * C3_SLOTS`). +fn c3_fold_total_slots_from_compiled_json() -> usize { + let path = c3_fold_json_path(); + let raw = std::fs::read_to_string(&path).unwrap_or_else(|e| { + panic!( + "read {}: {} (run `pnpm build:circuits --group recursive_aggregation`)", + path.display(), + e + ) + }); + let v: serde_json::Value = + serde_json::from_str(&raw).unwrap_or_else(|e| panic!("parse {}: {}", path.display(), e)); + let len = v["abi"]["parameters"] + .as_array() + .and_then(|ps| { + ps.iter() + .find(|p| { + p.get("name") == Some(&serde_json::Value::String("acc_public_inputs".into())) + }) + .and_then(|p| p.get("type")?.get("length")?.as_u64()) + }) + .expect("c3_fold.json: abi.parameters.acc_public_inputs.length") as usize; + assert!( + len >= 4 && (len - 4) % 3 == 0, + "unexpected acc_public_inputs length {} (expected 4 + 3 * slots)", + len + ); + (len - 4) / 3 +} + +/// Reads slot count from the compiled `c6_fold` ABI (`acc_public_inputs` length is `4 + 4 * slots`). +fn c6_fold_total_slots_from_compiled_json() -> usize { + let path = c6_fold_json_path(); + let raw = std::fs::read_to_string(&path).unwrap_or_else(|e| { + panic!( + "read {}: {} (run `pnpm build:circuits --group recursive_aggregation`)", + path.display(), + e + ) + }); + let v: serde_json::Value = + serde_json::from_str(&raw).unwrap_or_else(|e| panic!("parse {}: {}", path.display(), e)); + let len = v["abi"]["parameters"] + .as_array() + .and_then(|ps| { + ps.iter() + .find(|p| { + p.get("name") == Some(&serde_json::Value::String("acc_public_inputs".into())) + }) + .and_then(|p| p.get("type")?.get("length")?.as_u64()) + }) + .expect("c6_fold.json: abi.parameters.acc_public_inputs.length") as usize; + assert!( + len >= 4 && (len - 4) % 4 == 0, + "unexpected acc_public_inputs length {} (expected 4 + 4 * slots)", + len + ); + (len - 4) / 4 +} + +#[test] +fn c3_fold_compiled_abi_has_consistent_slot_count() { + if !c3_fold_json_path().exists() { + println!( + "skipping: {} not found (run `pnpm build:circuits --group recursive_aggregation`)", + c3_fold_json_path().display() + ); + return; + } + let slots = c3_fold_total_slots_from_compiled_json(); + assert!(slots > 0, "C3_SLOTS inferred from ABI should be positive"); + let _ = + CompiledCircuit::from_file(&c3_fold_json_path()).expect("load compiled c3_fold circuit"); +} + +#[test] +fn c6_fold_compiled_abi_has_consistent_slot_count() { + if !c6_fold_json_path().exists() { + println!( + "skipping: {} not found (run `pnpm build:circuits --group recursive_aggregation`)", + c6_fold_json_path().display() + ); + return; + } + let slots = c6_fold_total_slots_from_compiled_json(); + assert!(slots > 0, "C6 slots inferred from ABI should be positive"); + let _ = + CompiledCircuit::from_file(&c6_fold_json_path()).expect("load compiled c6_fold circuit"); +} + +#[test] +fn node_fold_pipeline_compiled_json_load() { + let mut missing = Vec::new(); + for &c in NODE_FOLD_PIPELINE { + let p = recursive_aggregation_compiled_json_path(c); + if !p.exists() { + missing.push(p); + } + } + if !missing.is_empty() { + println!( + "skipping: missing compiled JSON(s) (run `pnpm build:circuits --group recursive_aggregation`): {:?}", + missing + ); + return; + } + for &c in NODE_FOLD_PIPELINE { + let path = recursive_aggregation_compiled_json_path(c); + let _ = CompiledCircuit::from_file(&path) + .unwrap_or_else(|e| panic!("load compiled {}: {}", c.as_str(), e)); + } +} + +#[tokio::test] +async fn recursive_aggregation_default_artifacts_staged() { + let Some(bb) = find_bb().await else { + println!("skipping: bb not found"); + return; + }; + if !c3_fold_json_path().exists() { + println!("skipping: {} not found", c3_fold_json_path().display()); + return; + } + + let (backend, temp) = setup_test_prover(&bb).await; + setup_recursive_aggregation_fold_circuit(&backend, CircuitName::C3Fold).await; + + let base = backend + .circuits_dir + .join("insecure-512") + .join("default") + .join(CircuitName::C3Fold.dir_path()); + let pkg = CircuitName::C3Fold.as_str(); + assert!( + base.join(format!("{pkg}.json")).exists(), + "expected staged {}.json under default/ variant", + pkg + ); + assert!( + base.join(format!("{pkg}.vk")).exists(), + "expected staged {}.vk (noir-recursive-no-zk) under default/ variant", + pkg + ); + + drop(temp); +} + +#[tokio::test] +async fn recursive_aggregation_c6_fold_kernel_artifacts_staged() { + let Some(bb) = find_bb().await else { + println!("skipping: bb not found"); + return; + }; + let kernel_json = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../circuits/bin/recursive_aggregation/c6_fold_kernel/target/c6_fold_kernel.json"); + if !kernel_json.exists() { + println!("skipping: {} not found", kernel_json.display()); + return; + } + + let (backend, temp) = setup_test_prover(&bb).await; + setup_recursive_aggregation_fold_circuit(&backend, CircuitName::C6FoldKernel).await; + + let base = backend + .circuits_dir + .join("insecure-512") + .join("default") + .join(CircuitName::C6FoldKernel.dir_path()); + let pkg = CircuitName::C6FoldKernel.as_str(); + assert!( + base.join(format!("{pkg}.json")).exists(), + "expected staged {}.json under default/ variant", + pkg + ); + assert!( + base.join(format!("{pkg}.vk")).exists(), + "expected staged {}.vk (noir-recursive-no-zk) under default/ variant", + pkg + ); + + drop(temp); +} + +#[tokio::test] +async fn node_fold_pipeline_recursive_aggregation_artifacts_staged() { + let Some(bb) = find_bb().await else { + println!("skipping: bb not found"); + return; + }; + let gate = recursive_aggregation_compiled_json_path(CircuitName::NodeFold); + if !gate.exists() { + println!( + "skipping: {} not found (run `pnpm build:circuits --group recursive_aggregation`)", + gate.display() + ); + return; + } + + let (backend, temp) = setup_test_prover(&bb).await; + for &c in NODE_FOLD_PIPELINE { + setup_recursive_aggregation_fold_circuit(&backend, c).await; + } + + let preset_base = backend.circuits_dir.join("insecure-512").join("default"); + for &c in NODE_FOLD_PIPELINE { + let base = preset_base.join(c.dir_path()); + let pkg = c.as_str(); + assert!( + base.join(format!("{pkg}.json")).exists(), + "expected staged {}.json under default/ variant", + pkg + ); + assert!( + base.join(format!("{pkg}.vk")).exists(), + "expected staged {}.vk (noir-recursive-no-zk) under default/ variant", + pkg + ); + } + + drop(temp); +} + +async fn setup_c3_fold_with_inner_share_encryption() -> Option<( + ZkBackend, + tempfile::TempDir, + ZkProver, + ShareEncryptionCircuit, + ShareEncryptionCircuitData, + ShareEncryptionCircuitData, + BfvPreset, +)> { + let committee = CiphernodesCommitteeSize::Micro.values(); + let preset = BfvPreset::InsecureThreshold512; + let bb = find_bb().await?; + let (backend, temp) = setup_test_prover(&bb).await; + + let sd = BfvPreset::InsecureThreshold512.search_defaults()?; + + setup_compiled_circuit(&backend, "dkg", "share_encryption").await; + setup_recursive_aggregation_fold_circuit(&backend, CircuitName::C3Fold).await; + setup_recursive_aggregation_fold_circuit(&backend, CircuitName::C3FoldKernel).await; + + let sample_a = ShareEncryptionCircuitData::generate_sample( + preset, + committee.clone(), + DkgInputType::SecretKey, + sd.z, + sd.lambda, + ) + .ok()?; + let sample_b = ShareEncryptionCircuitData::generate_sample( + preset, + committee, + DkgInputType::SecretKey, + sd.z, + sd.lambda, + ) + .ok()?; + let prover = ZkProver::new(&backend); + + Some(( + backend, + temp, + prover, + ShareEncryptionCircuit, + sample_a, + sample_b, + preset, + )) +} + +#[tokio::test] +async fn c3_fold_sequential_proves_and_verifies() { + let Some((_backend, _temp, prover, circuit, sample_a, sample_b, preset)) = + setup_c3_fold_with_inner_share_encryption().await + else { + println!("skipping: bb not found or prerequisites missing"); + return; + }; + + let artifacts_dir = preset.artifacts_dir(); + let inner_e3_a = "e3-c3fold-inner-0"; + let inner_e3_b = "e3-c3fold-inner-1"; + let fold_e3 = "e3-c3fold-step"; + + let inner_a = circuit + .prove_with_variant( + &prover, + &preset, + &sample_a, + inner_e3_a, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("inner ShareEncryption proof 0"); + assert_eq!(inner_a.circuit, CircuitName::ShareEncryption); + + let inner_b = circuit + .prove_with_variant( + &prover, + &preset, + &sample_b, + inner_e3_b, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("inner ShareEncryption proof 1"); + assert_eq!(inner_b.circuit, CircuitName::ShareEncryption); + + let total_slots = c3_fold_total_slots_from_compiled_json(); + assert!( + total_slots >= 2, + "need at least 2 C3 slots for two-fold test (compiled total_slots={})", + total_slots + ); + + let inners = [inner_a, inner_b]; + let folded = generate_sequential_c3_fold( + &prover, + &inners, + &[0u32, 1u32], + total_slots, + fold_e3, + &artifacts_dir, + ) + .expect("c3_fold sequential fold"); + assert_eq!(folded.circuit, CircuitName::C3Fold); + assert!( + !folded.data.is_empty(), + "fold proof data should not be empty" + ); + assert!( + !folded.public_signals.is_empty(), + "fold public signals should not be empty" + ); + + let party_id = 1u64; + let ok = prover + .verify_fold_proof(&folded, fold_e3, party_id, &artifacts_dir) + .expect("verify_fold_proof invocation"); + assert!(ok, "c3_fold proof should verify under Default VK layout"); + + prover.cleanup(inner_e3_a).unwrap(); + prover.cleanup(inner_e3_b).unwrap(); + prover.cleanup(fold_e3).unwrap(); +} + +async fn setup_c6_fold_with_inner_threshold_share_decryption() -> Option<( + ZkBackend, + tempfile::TempDir, + ZkProver, + ShareDecryptionCircuit, + ShareDecryptionCircuitData, + ShareDecryptionCircuitData, + BfvPreset, +)> { + let committee = CiphernodesCommitteeSize::Micro.values(); + let preset = BfvPreset::InsecureThreshold512; + let bb = find_bb().await?; + let (backend, temp) = setup_test_prover(&bb).await; + + setup_compiled_circuit(&backend, "threshold", "share_decryption").await; + setup_recursive_aggregation_fold_circuit(&backend, CircuitName::C6Fold).await; + setup_recursive_aggregation_fold_circuit(&backend, CircuitName::C6FoldKernel).await; + + let sample_a = ShareDecryptionCircuitData::generate_sample(preset, committee.clone()).ok()?; + let sample_b = ShareDecryptionCircuitData::generate_sample(preset, committee).ok()?; + let prover = ZkProver::new(&backend); + + Some(( + backend, + temp, + prover, + ShareDecryptionCircuit, + sample_a, + sample_b, + preset, + )) +} + +#[tokio::test] +async fn c6_fold_sequential_proves_and_verifies() { + let Some((_backend, _temp, prover, circuit, sample_a, sample_b, preset)) = + setup_c6_fold_with_inner_threshold_share_decryption().await + else { + println!("skipping: bb not found or prerequisites missing"); + return; + }; + + let artifacts_dir = preset.artifacts_dir(); + let inner_e3_a = "e3-c6fold-inner-0"; + let inner_e3_b = "e3-c6fold-inner-1"; + let fold_e3 = "e3-c6fold-step"; + + let inner_a = circuit + .prove_with_variant( + &prover, + &preset, + &sample_a, + inner_e3_a, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("inner ThresholdShareDecryption proof 0"); + assert_eq!(inner_a.circuit, CircuitName::ThresholdShareDecryption); + + let inner_b = circuit + .prove_with_variant( + &prover, + &preset, + &sample_b, + inner_e3_b, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("inner ThresholdShareDecryption proof 1"); + assert_eq!(inner_b.circuit, CircuitName::ThresholdShareDecryption); + + let total_slots = c6_fold_total_slots_from_compiled_json(); + assert!( + total_slots >= 2, + "need at least 2 C6 slots for two-fold test (compiled total_slots={})", + total_slots + ); + + let inners = [inner_a, inner_b]; + let folded = generate_sequential_c6_fold( + &prover, + &inners, + &[0u32, 1u32], + total_slots, + fold_e3, + &artifacts_dir, + ) + .expect("c6_fold sequential fold"); + assert_eq!(folded.circuit, CircuitName::C6Fold); + assert!( + !folded.data.is_empty(), + "fold proof data should not be empty" + ); + assert!( + !folded.public_signals.is_empty(), + "fold public signals should not be empty" + ); + + let party_id = 1u64; + let ok = prover + .verify_fold_proof(&folded, fold_e3, party_id, &artifacts_dir) + .expect("verify_fold_proof invocation"); + assert!(ok, "c6_fold proof should verify under Default VK layout"); + + prover.cleanup(inner_e3_a).unwrap(); + prover.cleanup(inner_e3_b).unwrap(); + prover.cleanup(fold_e3).unwrap(); +} diff --git a/crates/zk-prover/tests/local_e2e_tests.rs b/crates/zk-prover/tests/local_e2e_tests.rs index a593e6d239..52f49f33c0 100644 --- a/crates/zk-prover/tests/local_e2e_tests.rs +++ b/crates/zk-prover/tests/local_e2e_tests.rs @@ -11,7 +11,7 @@ //! produced by `pnpm build:circuits` locally or the `build_circuits` CI job. //! //! To add a new circuit: add setup_*_test() and one line in `e2e_proof_tests!` -//! `(name, setup, CircuitVariant::Recursive | Evm)` (C5 pk_aggregation uses Evm). +//! `(name, setup, CircuitVariant::...)` (C5 uses Default / noir-recursive-no-zk; EVM is `DkgAggregator`). //! Commitment consistency tests are defined separately. mod common; @@ -500,7 +500,7 @@ e2e_proof_tests! { (share_encryption_sk, setup_share_encryption_sk_test(), CircuitVariant::Recursive), (share_encryption_e_sm, setup_share_encryption_e_sm_test(), CircuitVariant::Recursive), (share_decryption, setup_share_decryption_test(), CircuitVariant::Recursive), - (pk_aggregation, setup_pk_aggregation_test(), CircuitVariant::Evm), + (pk_aggregation, setup_pk_aggregation_test(), CircuitVariant::Default), (decrypted_shares_aggregation, setup_decrypted_shares_aggregation_test(), CircuitVariant::Recursive), } @@ -666,8 +666,6 @@ async fn test_pk_aggregation_commitment_consistency() { return; }; - // C5 uses Evm variant in production; Recursive fails because commitment hashes (256-bit) - // exceed the noir-recursive verifier's limb bound. let artifacts_dir = preset.artifacts_dir(); let proof = circuit .prove_with_variant( @@ -675,7 +673,7 @@ async fn test_pk_aggregation_commitment_consistency() { &preset, &sample, e3_id, - CircuitVariant::Evm, + CircuitVariant::Default, &artifacts_dir, ) .expect("proof generation should succeed"); @@ -734,15 +732,15 @@ async fn test_threshold_share_decryption_commitment_consistency() { let computation_output = ThresholdShareDecryptionCircuit::compute(preset, &sample).unwrap(); let expected_d_commitment = compute_threshold_decryption_share_commitment( - &computation_output.inputs.d, - computation_output.bits.d_bit, + &computation_output.inputs.d_native_trunc, + computation_output.bits.d_native_bit, MAX_MSG_NON_ZERO_COEFFS, ); let d_commitment_from_proof = extract_field_from_end(&proof.public_signals, 0); assert_eq!( d_commitment_from_proof, expected_d_commitment, - "C6 d_commitment must match compute_threshold_decryption_share_commitment on witness d" + "C6 d_commitment must match hash of d_native_trunc (C7-aligned), not centered witness d" ); prover.cleanup(e3_id).unwrap(); diff --git a/crates/zk-prover/tests/node_fold_correlated_e2e_tests.rs b/crates/zk-prover/tests/node_fold_correlated_e2e_tests.rs new file mode 100644 index 0000000000..aacac9c409 --- /dev/null +++ b/crates/zk-prover/tests/node_fold_correlated_e2e_tests.rs @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Correlated `node_fold` proof: one [`PkGenerationCircuitData`] drives C1 and both C2 chains; C3 +//! inner proofs use [`node_fold_witness::share_encryption_for_slot`] (`tests/common/node_fold_witness.rs`); C4 reuses one honest row for +//! all `H` senders so decryption witnesses stay self-consistent. +//! +//! Requires `bb`, `pnpm build:circuits --group recursive_aggregation`, and DKG/threshold bins. + +mod common; +#[path = "common/node_fold_witness.rs"] +mod node_fold_witness; + +use std::path::PathBuf; + +use common::{ + find_bb, setup_compiled_circuit, setup_recursive_aggregation_fold_circuit, setup_test_prover, +}; +use e3_events::{CircuitName, Proof}; +use e3_fhe_params::BfvPreset; +use e3_zk_helpers::computation::Computation; +use e3_zk_helpers::computation::DkgInputType; +use e3_zk_helpers::dkg::pk::circuit::{PkCircuit, PkCircuitData}; +use e3_zk_helpers::dkg::share_computation::{ + Inputs as ShareComputationInputs, ShareComputationCircuit, +}; +use e3_zk_helpers::dkg::share_decryption::{ShareDecryptionCircuit, ShareDecryptionCircuitData}; +use e3_zk_helpers::dkg::share_encryption::ShareEncryptionCircuit; +use e3_zk_helpers::threshold::pk_generation::PkGenerationCircuit; +use e3_zk_helpers::CiphernodesCommitteeSize; +use e3_zk_prover::test_utils::{ + fold_witness_field_strings, fold_witness_input_map, load_vk_artifacts, +}; +use e3_zk_prover::{generate_sequential_c3_fold, CircuitVariant, Provable, ZkProver}; +use e3_zk_prover::{CompiledCircuit, WitnessGenerator}; +use node_fold_witness::{ + pk_generation_sample_with_esi, share_computation_esm_from_esi, share_computation_sk_from_pk, + share_encryption_for_slot, +}; +use serde::Serialize; +use serde_json::json; + +fn recursive_aggregation_compiled_json_path(circuit: CircuitName) -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../circuits/bin") + .join(circuit.group()) + .join(circuit.as_str()) + .join("target") + .join(format!("{}.json", circuit.as_str())) +} + +fn c3_fold_json_path() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../circuits/bin/recursive_aggregation/c3_fold/target/c3_fold.json") +} + +fn c3_fold_total_slots_from_compiled_json() -> usize { + let path = c3_fold_json_path(); + let raw = + std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {}: {}", path.display(), e)); + let v: serde_json::Value = serde_json::from_str(&raw).unwrap(); + let len = v["abi"]["parameters"] + .as_array() + .and_then(|ps| { + ps.iter() + .find(|p| { + p.get("name") == Some(&serde_json::Value::String("acc_public_inputs".into())) + }) + .and_then(|p| p.get("type")?.get("length")?.as_u64()) + }) + .expect("c3_fold acc_public_inputs length") as usize; + (len - 4) / 3 +} + +fn field_str_zero() -> String { + format!("0x{}", hex::encode([0u8; 32])) +} + +fn proof_public_fields(proof: &Proof) -> Vec { + fold_witness_field_strings(proof.public_signals.as_ref()).expect("public_signals as fields") +} + +#[derive(Serialize)] +struct C2abFoldWitness { + c2a_vk: Vec, + c2a_proof: Vec, + c2a_public: Vec, + c2b_vk: Vec, + c2b_proof: Vec, + c2b_public: Vec, + c2a_key_hash: String, + c2b_key_hash: String, +} + +#[derive(Serialize)] +struct C3abFoldWitness { + c3a_vk: Vec, + c3a_proof: Vec, + c3a_public: Vec, + c3b_vk: Vec, + c3b_proof: Vec, + c3b_public: Vec, + c3a_key_hash: String, + c3b_key_hash: String, +} + +#[derive(Serialize)] +struct C4abFoldWitness { + c4a_vk: Vec, + c4a_proof: Vec, + c4a_public: Vec, + c4b_vk: Vec, + c4b_proof: Vec, + c4b_public: Vec, + c4a_key_hash: String, + c4b_key_hash: String, +} + +fn triplicate_honest_rows(mut d: ShareDecryptionCircuitData) -> ShareDecryptionCircuitData { + let row0 = d.honest_ciphertexts[0].clone(); + d.honest_ciphertexts = (0..d.honest_ciphertexts.len()) + .map(|_| row0.clone()) + .collect(); + d +} + +#[tokio::test] +async fn node_fold_correlated_proves_and_verifies() { + let Some(bb) = find_bb().await else { + println!("skipping: bb not found"); + return; + }; + + let gate = recursive_aggregation_compiled_json_path(CircuitName::NodeFold); + if !gate.exists() { + println!( + "skipping: {} not found (run `pnpm build:circuits --group recursive_aggregation`)", + gate.display() + ); + return; + } + if !c3_fold_json_path().exists() { + println!("skipping: c3_fold.json not found"); + return; + } + + let committee = CiphernodesCommitteeSize::Micro.values(); + let preset = BfvPreset::InsecureThreshold512; + + let (backend, temp) = setup_test_prover(&bb).await; + let prover = ZkProver::new(&backend); + let artifacts_dir = preset.artifacts_dir(); + + for g in [ + "pk", + "sk_share_computation", + "e_sm_share_computation", + "share_encryption", + "share_decryption", + ] { + let name = match g { + "pk" => "pk", + "sk_share_computation" => "sk_share_computation", + "e_sm_share_computation" => "e_sm_share_computation", + "share_encryption" => "share_encryption", + "share_decryption" => "share_decryption", + _ => unreachable!(), + }; + setup_compiled_circuit(&backend, "dkg", name).await; + } + setup_compiled_circuit(&backend, "threshold", "pk_generation").await; + + for c in [ + CircuitName::C2abFold, + CircuitName::C3Fold, + CircuitName::C3FoldKernel, + CircuitName::C3abFold, + CircuitName::C4abFold, + CircuitName::NodeFold, + ] { + setup_recursive_aggregation_fold_circuit(&backend, c).await; + } + + let (pk_gen, esi, pk_secret_key) = pk_generation_sample_with_esi(preset, committee.clone()) + .expect("pk + esi correlated sample"); + let share_sk = share_computation_sk_from_pk(preset, committee.clone(), &pk_gen, &pk_secret_key) + .expect("correlated C2a data"); + let share_esm = share_computation_esm_from_esi(preset, committee.clone(), &pk_gen, &esi) + .expect("correlated C2b data"); + + let sk_inputs = ShareComputationInputs::compute(preset, &share_sk).expect("C2a inputs"); + let esm_inputs = ShareComputationInputs::compute(preset, &share_esm).expect("C2b inputs"); + + let pk_bfv_data = PkCircuitData::generate_sample(preset).expect("C0 pk sample"); + let c0_e3 = "e3-nf-c0"; + let c1_e3 = "e3-nf-c1"; + let c2a_e3 = "e3-nf-c2a"; + let c2b_e3 = "e3-nf-c2b"; + let c2ab_e3 = "e3-nf-c2ab"; + + let c0_proof = PkCircuit + .prove_with_variant( + &prover, + &preset, + &pk_bfv_data, + c0_e3, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("C0 pk proof"); + let c1_proof = PkGenerationCircuit + .prove_with_variant( + &prover, + &preset, + &pk_gen, + c1_e3, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("C1 pk_generation proof"); + + let c2a_proof = ShareComputationCircuit + .prove_with_variant( + &prover, + &preset, + &share_sk, + c2a_e3, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("C2a proof"); + let c2b_proof = ShareComputationCircuit + .prove_with_variant( + &prover, + &preset, + &share_esm, + c2b_e3, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("C2b proof"); + + let c2a_vk = load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), + CircuitName::SkShareComputation, + ) + .expect("c2a vk"); + let c2b_vk = load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), + CircuitName::ESmShareComputation, + ) + .expect("c2b vk"); + + let c2a_pub = proof_public_fields(&c2a_proof); + let c2b_pub = proof_public_fields(&c2b_proof); + let c2ab = C2abFoldWitness { + c2a_vk: c2a_vk.verification_key, + c2a_proof: fold_witness_field_strings(&c2a_proof.data).expect("c2a proof fields"), + c2a_public: c2a_pub.clone(), + c2b_vk: c2b_vk.verification_key, + c2b_proof: fold_witness_field_strings(&c2b_proof.data).expect("c2b proof fields"), + c2b_public: c2b_pub.clone(), + c2a_key_hash: c2a_vk.key_hash.clone(), + c2b_key_hash: c2b_vk.key_hash.clone(), + }; + + let c2ab_json = serde_json::to_value(&c2ab).expect("c2ab json"); + let c2ab_map = fold_witness_input_map(&c2ab_json).expect("c2ab input map"); + let c2ab_compiled = CompiledCircuit::from_file( + &prover + .circuits_dir(CircuitVariant::Default, &artifacts_dir) + .join(CircuitName::C2abFold.dir_path()) + .join(format!("{}.json", CircuitName::C2abFold.as_str())), + ) + .expect("c2ab compiled"); + let c2ab_witness = WitnessGenerator::new() + .generate_witness(&c2ab_compiled, c2ab_map) + .expect("c2ab witness"); + let c2ab_proof = prover + .generate_recursive_aggregation_bin_proof( + CircuitName::C2abFold, + &c2ab_witness, + c2ab_e3, + &artifacts_dir, + ) + .expect("c2ab_fold proof"); + + let (_dkg_th, dkg_dkg) = e3_fhe_params::build_pair_for_preset(preset).expect("pair"); + let mut rng = rand::thread_rng(); + let dkg_sk = fhe::bfv::SecretKey::random(&dkg_dkg, &mut rng); + let dkg_pk = fhe::bfv::PublicKey::new(&dkg_sk, &mut rng); + + let total_slots = c3_fold_total_slots_from_compiled_json(); + assert_eq!(total_slots, 6, "Micro / insecure preset uses 3×2 C3 slots"); + + let mut c3a_inners = Vec::new(); + let mut c3b_inners = Vec::new(); + for slot in 0..total_slots { + let da = share_encryption_for_slot( + preset, + &dkg_sk, + &dkg_pk, + &sk_inputs, + slot, + DkgInputType::SecretKey, + ) + .expect("C3a slot encrypt"); + let db = share_encryption_for_slot( + preset, + &dkg_sk, + &dkg_pk, + &esm_inputs, + slot, + DkgInputType::SmudgingNoise, + ) + .expect("C3b slot encrypt"); + + c3a_inners.push( + ShareEncryptionCircuit + .prove_with_variant( + &prover, + &preset, + &da, + &format!("e3-nf-c3a-{slot}"), + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("C3a inner"), + ); + c3b_inners.push( + ShareEncryptionCircuit + .prove_with_variant( + &prover, + &preset, + &db, + &format!("e3-nf-c3b-{slot}"), + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("C3b inner"), + ); + } + + let slot_indices: Vec = (0..total_slots as u32).collect(); + let c3a_folded = generate_sequential_c3_fold( + &prover, + &c3a_inners, + &slot_indices, + total_slots, + "e3-nf-c3fold-a", + &artifacts_dir, + ) + .expect("c3 fold sk chain"); + let c3b_folded = generate_sequential_c3_fold( + &prover, + &c3b_inners, + &slot_indices, + total_slots, + "e3-nf-c3fold-b", + &artifacts_dir, + ) + .expect("c3 fold e_sm chain"); + + let c3a_vk = load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, &artifacts_dir), + CircuitName::C3Fold, + ) + .expect("c3a fold vk"); + let c3b_vk = load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, &artifacts_dir), + CircuitName::C3Fold, + ) + .expect("c3b fold vk"); + + let c3a_pub = proof_public_fields(&c3a_folded); + let c3b_pub = proof_public_fields(&c3b_folded); + let c3ab = C3abFoldWitness { + c3a_vk: c3a_vk.verification_key, + c3a_proof: fold_witness_field_strings(&c3a_folded.data).expect("c3a fold proof"), + c3a_public: c3a_pub, + c3b_vk: c3b_vk.verification_key, + c3b_proof: fold_witness_field_strings(&c3b_folded.data).expect("c3b fold proof"), + c3b_public: c3b_pub, + c3a_key_hash: c3a_vk.key_hash.clone(), + c3b_key_hash: c3b_vk.key_hash.clone(), + }; + let c3ab_json = serde_json::to_value(&c3ab).expect("c3ab json"); + let c3ab_map = fold_witness_input_map(&c3ab_json).expect("c3ab map"); + let c3ab_compiled = CompiledCircuit::from_file( + &prover + .circuits_dir(CircuitVariant::Default, &artifacts_dir) + .join(CircuitName::C3abFold.dir_path()) + .join(format!("{}.json", CircuitName::C3abFold.as_str())), + ) + .expect("c3ab compiled"); + let c3ab_witness = WitnessGenerator::new() + .generate_witness(&c3ab_compiled, c3ab_map) + .expect("c3ab witness"); + let c3ab_proof = prover + .generate_recursive_aggregation_bin_proof( + CircuitName::C3abFold, + &c3ab_witness, + "e3-nf-c3ab", + &artifacts_dir, + ) + .expect("c3ab_fold proof"); + + let c4a_sample = ShareDecryptionCircuitData::generate_sample( + preset, + committee.clone(), + DkgInputType::SecretKey, + ) + .expect("c4a sample"); + let c4b_sample = ShareDecryptionCircuitData::generate_sample( + preset, + committee.clone(), + DkgInputType::SmudgingNoise, + ) + .expect("c4b sample"); + let c4a_data = triplicate_honest_rows(c4a_sample); + let c4b_data = triplicate_honest_rows(c4b_sample); + + let c4a_e3 = "e3-nf-c4a"; + let c4b_e3 = "e3-nf-c4b"; + let c4a_proof = ShareDecryptionCircuit + .prove_with_variant( + &prover, + &preset, + &c4a_data, + c4a_e3, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("C4a"); + let c4b_proof = ShareDecryptionCircuit + .prove_with_variant( + &prover, + &preset, + &c4b_data, + c4b_e3, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("C4b"); + + let c4a_vk = load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), + CircuitName::DkgShareDecryption, + ) + .expect("c4a vk"); + let c4b_vk = load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), + CircuitName::DkgShareDecryption, + ) + .expect("c4b vk"); + + let c4ab = C4abFoldWitness { + c4a_vk: c4a_vk.verification_key, + c4a_proof: fold_witness_field_strings(&c4a_proof.data).expect("c4a"), + c4a_public: proof_public_fields(&c4a_proof), + c4b_vk: c4b_vk.verification_key, + c4b_proof: fold_witness_field_strings(&c4b_proof.data).expect("c4b"), + c4b_public: proof_public_fields(&c4b_proof), + c4a_key_hash: c4a_vk.key_hash.clone(), + c4b_key_hash: c4b_vk.key_hash.clone(), + }; + let c4ab_json = serde_json::to_value(&c4ab).expect("c4ab json"); + let c4ab_map = fold_witness_input_map(&c4ab_json).expect("c4ab map"); + let c4ab_compiled = CompiledCircuit::from_file( + &prover + .circuits_dir(CircuitVariant::Default, &artifacts_dir) + .join(CircuitName::C4abFold.dir_path()) + .join(format!("{}.json", CircuitName::C4abFold.as_str())), + ) + .expect("c4ab compiled"); + let c4ab_witness = WitnessGenerator::new() + .generate_witness(&c4ab_compiled, c4ab_map) + .expect("c4ab witness"); + let c4ab_proof = prover + .generate_recursive_aggregation_bin_proof( + CircuitName::C4abFold, + &c4ab_witness, + "e3-nf-c4ab", + &artifacts_dir, + ) + .expect("c4ab_fold proof"); + + let c0_vk = load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), + CircuitName::PkBfv, + ) + .expect("c0 vk"); + let c1_vk = load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), + CircuitName::PkGeneration, + ) + .expect("c1 vk"); + let c2ab_fold_vk = load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, &artifacts_dir), + CircuitName::C2abFold, + ) + .expect("c2ab fold vk"); + let c3ab_fold_vk = load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, &artifacts_dir), + CircuitName::C3abFold, + ) + .expect("c3ab fold vk"); + let c4ab_fold_vk = load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, &artifacts_dir), + CircuitName::C4abFold, + ) + .expect("c4ab fold vk"); + + let nf = json!({ + "c0_vk": c0_vk.verification_key, + "c0_proof": fold_witness_field_strings(&c0_proof.data).expect("c0 proof"), + "c0_public": proof_public_fields(&c0_proof), + "c1_vk": c1_vk.verification_key, + "c1_proof": fold_witness_field_strings(&c1_proof.data).expect("c1 proof"), + "c1_public": proof_public_fields(&c1_proof), + "c2ab_vk": c2ab_fold_vk.verification_key, + "c2ab_proof": fold_witness_field_strings(&c2ab_proof.data).expect("c2ab"), + "c2ab_public": proof_public_fields(&c2ab_proof), + "c3ab_vk": c3ab_fold_vk.verification_key, + "c3ab_proof": fold_witness_field_strings(&c3ab_proof.data).expect("c3ab"), + "c3ab_public": proof_public_fields(&c3ab_proof), + "c4ab_vk": c4ab_fold_vk.verification_key, + "c4ab_proof": fold_witness_field_strings(&c4ab_proof.data).expect("c4ab"), + "c4ab_public": proof_public_fields(&c4ab_proof), + "party_id": field_str_zero(), + "c0_key_hash": c0_vk.key_hash, + "c1_key_hash": c1_vk.key_hash, + "c2ab_key_hash": c2ab_fold_vk.key_hash, + "c3ab_key_hash": c3ab_fold_vk.key_hash, + "c4ab_key_hash": c4ab_fold_vk.key_hash, + }); + + let nf_map = fold_witness_input_map(&nf).expect("node_fold map"); + let nf_compiled = CompiledCircuit::from_file(&gate).expect("node_fold compiled"); + let nf_witness = WitnessGenerator::new() + .generate_witness(&nf_compiled, nf_map) + .expect("node_fold witness"); + let nf_proof = prover + .generate_recursive_aggregation_bin_proof( + CircuitName::NodeFold, + &nf_witness, + "e3-nf-node", + &artifacts_dir, + ) + .expect("node_fold proof"); + + let ok = prover + .verify_fold_proof(&nf_proof, "e3-nf-node", 0, &artifacts_dir) + .expect("verify node_fold"); + assert!(ok, "node_fold should verify"); + + drop(temp); +} diff --git a/docs/pages/ciphernode-operators/index.mdx b/docs/pages/ciphernode-operators/index.mdx index 064da3c17a..672b3b5072 100644 --- a/docs/pages/ciphernode-operators/index.mdx +++ b/docs/pages/ciphernode-operators/index.mdx @@ -50,15 +50,14 @@ The Interfold protocol uses several contracts that work together: | EnclaveToken (ENCL) | `0x24b28471AE7BdF1fdBcfDd183c73D13ff0689B99` | 10395612 | | MockUSDC (fee token) | `0x01AbD7D8e6547c943c2fE082C3Ce194fDCe57396` | 10395611 | -Circuit verifiers (C5, C7, Fold) are deployed with the main flow. The DecryptedSharesAggregation -circuit (C7) is deployed as a single ThresholdDecryptedSharesAggregationVerifier. See -`deployed_contracts.json` for addresses. - -| Verifier | Address | -| ------------------------------------------------ | ------------------------------------------ | -| ThresholdPkAggregationVerifier (C5) | See `deployed_contracts.json` after deploy | -| ThresholdDecryptedSharesAggregationVerifier (C7) | See `deployed_contracts.json` after deploy | -| RecursiveAggregationFoldVerifier (Fold) | See `deployed_contracts.json` after deploy | +The DKG and decryption aggregator circuits are deployed as the sole on-chain verifiers for BFV +public-key publication and plaintext decryption respectively (each aggregator internally verifies +the underlying node-fold/C5 and C6-fold/C7 sub-proofs). See `deployed_contracts.json` for addresses. + +| Verifier | Address | +| --------------------------------- | ------------------------------------------ | +| DkgAggregatorVerifier (DKG) | See `deployed_contracts.json` after deploy | +| DecryptionAggregatorVerifier (C7) | See `deployed_contracts.json` after deploy | > Always verify addresses from `packages/enclave-contracts/deployed_contracts.json` or your > deployment output. Addresses differ per network. diff --git a/docs/pages/cryptography.mdx b/docs/pages/cryptography.mdx index 29c43e7b5c..1740456a14 100644 --- a/docs/pages/cryptography.mdx +++ b/docs/pages/cryptography.mdx @@ -105,13 +105,13 @@ reading the code: it connects the narrative phases P1–P4 to the **C0–C7** la logs, and the [`circuits/README.md`](https://github.com/gnosisguild/enclave/blob/main/circuits/README.md) index. -| Phase | Circuits | What you are looking at | -| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -| **Config** | [`config`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/config) | One-time consistency of presets before any E3. | -| **P1 — DKG** | [**C0**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/pk), [**C1**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_generation), [**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation), [**C3**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_encryption), [**C4**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_decryption) | Individual pk; TrBFV contribution; Shamir + parity (recursive **C2**); encrypt shares; decrypt and aggregate. | -| **P2 — Aggregation** | [**C5**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_aggregation) | Sum honest public key shares into the threshold pk. | -| **P3 — User encryption** | [`user_data_encryption_*`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/user_data_encryption_ct0) | Valid BFV encryption under the aggregated key. | -| **P4 — Decryption** | [**C6**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/share_decryption), [**C7**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/decrypted_shares_aggregation) | Partial decryptions; Lagrange combination, CRT lift, decode to plaintext. | +| Phase | Circuits | What you are looking at | +| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| **Config** | [`config`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/config) | One-time consistency of presets before any E3. | +| **P1 — DKG** | [**C0**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/pk), [**C1**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_generation), [**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/sk_share_computation), [**C3**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_encryption), [**C4**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_decryption) | Individual pk; TrBFV contribution; Shamir + parity (recursive **C2**); encrypt shares; decrypt and aggregate. | +| **P2 — Aggregation** | [**C5**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_aggregation) | Sum honest public key shares into the threshold pk. | +| **P3 — User encryption** | [`user_data_encryption_*`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/user_data_encryption_ct0) | Valid BFV encryption under the aggregated key. | +| **P4 — Decryption** | [**C6**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/share_decryption), [**C7**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/decrypted_shares_aggregation) | Partial decryptions; Lagrange combination, CRT lift, decode to plaintext. | ### Sequence diagram @@ -203,7 +203,7 @@ Individual circuit proofs cover each step in isolation, but the protocol must la a compact, verifiable package. A naive fold of all proofs into one recursive aggregate produces an opaque commitment that the contract cannot interpret without the original public inputs, and if cross-circuit consistency (e.g. that -[**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_computation)'s +[**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/sk_share_computation)'s expected commitments match [**C1**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_generation)'s outputs) is only checked off-chain, end-to-end correctness still depends on trusting the committee @@ -266,22 +266,22 @@ decryption actually rely on. Mixing those two notions is the most common source table below keeps the vocabulary straight. Treat it as a glossary you can return to while reading logs or Noir sources. -| Term | Meaning | -| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Individual key pair** | Per-node BFV keys used **only** to encrypt **Shamir shares** in transit during DKG. [**C0**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/pk) commits the individual public key; [**C3**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_encryption) binds encryption to that commitment. | -| **Secret key contribution** | One node’s additive piece of the threshold secret **before** Shamir splitting in the usual DKG story. | -| **Public key share** | The TrBFV public material for that contribution; [**C1**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_generation) proves BFV keygen relations; [**C5**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_aggregation) sums these shares. | -| **Secret key share** | After P1, a node’s **Shamir** share of the threshold secret (not the same as “contribution”). | -| **Threshold public key** | The BFV key users encrypt to after [**C5**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_aggregation); no single party holds the full matching secret. | -| **Smudging noise** | Noise shared across parties so decryption shares do not leak **sk**; **e_sm** in Noir; proved in [**C1**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_generation)–[**C4**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_decryption) and used in [**C6**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/share_decryption). | -| **Smudging noise contribution / share** | Per-node piece of smudging material and its Shamir shares, parallel to the secret-key track ([**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation) / [**C3**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_encryption) / [**C4**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_decryption) smudging track). | +| Term | Meaning | +| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Individual key pair** | Per-node BFV keys used **only** to encrypt **Shamir shares** in transit during DKG. [**C0**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/pk) commits the individual public key; [**C3**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_encryption) binds encryption to that commitment. | +| **Secret key contribution** | One node’s additive piece of the threshold secret **before** Shamir splitting in the usual DKG story. | +| **Public key share** | The TrBFV public material for that contribution; [**C1**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_generation) proves BFV keygen relations; [**C5**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_aggregation) sums these shares. | +| **Secret key share** | After P1, a node’s **Shamir** share of the threshold secret (not the same as “contribution”). | +| **Threshold public key** | The BFV key users encrypt to after [**C5**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_aggregation); no single party holds the full matching secret. | +| **Smudging noise** | Noise shared across parties so decryption shares do not leak **sk**; **e_sm** in Noir; proved in [**C1**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_generation)–[**C4**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_decryption) and used in [**C6**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/share_decryption). | +| **Smudging noise contribution / share** | Per-node piece of smudging material and its Shamir shares, parallel to the secret-key track ([**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/sk_share_computation) / [**C3**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_encryption) / [**C4**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_decryption) smudging track). | In Noir, [**C1**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_generation) names the BFV encryption-error polynomial **`eek`**. Ring dimension **N**, CRT limbs **L**, plaintext modulus **t**, and threshold **T** are fixed per deployment; the **PARITY_MATRIX** that constrains Shamir structure in -[**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation) +[**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/sk_share_computation) ships inside the secure preset that [`config`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/config) checks. @@ -304,7 +304,7 @@ Next, (`threshold/pk_generation`) proves the TrBFV contribution satisfies BFV key generation—bounded coefficients, Fiat–Shamir challenge points, Schwartz–Zippel style checks—and emits commitments that feed both the share pipeline -([**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation), +([**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/sk_share_computation), for secret and smudging tracks) and aggregation ([**C5**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_aggregation)). At one Fiat–Shamir challenge point γ (per CRT limb), the TrBFV public key legs are checked in the @@ -320,14 +320,14 @@ shape below (the implementation names the error polynomial **eek**): public-key leg is the CRS polynomial **a**, so **pk1**[\ell] = **a**[\ell] by construction—the circuit’s `verify_evaluations` only evaluates the **pk0** identity at γ.) -[**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation) -is not a single monolithic proof: the repository uses inner share-computation circuits +[**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/sk_share_computation) is +not a single monolithic proof: the repository uses inner share-computation circuits `sk_share_computation` and `e_sm_share_computation` to check Shamir-style sharing and Reed–Solomon -parity, and a recursive aggregation wrapper (`recursive_aggregation/wrapper/dkg/share_computation`) -to fold the batch proofs while keeping verifier work small. Intuitively, for each CRT limb and -coefficient column, the shares laid out as a vector **y** must satisfy parity with a fixed matrix -**H** so that only low-degree Shamir-style codewords pass (the exact **PARITY_MATRIX** is preset and -checked by `config`): +parity, and a recursive aggregation wrapper (`recursive_aggregation/c2ab_fold`) to fold the batch +proofs while keeping verifier work small. Intuitively, for each CRT limb and coefficient column, the +shares laid out as a vector **y** must satisfy parity with a fixed matrix **H** so that only +low-degree Shamir-style codewords pass (the exact **PARITY_MATRIX** is preset and checked by +`config`): ```math \mathbf{H}_j\,\mathbf{y}_{i,j}^{\mathsf{T}} \equiv \mathbf{0} \pmod{q_j} @@ -433,7 +433,7 @@ Across the stack you will see the same few ideas reused in different algebraic c **Schwartz–Zippel** lets the circuits test identities at random points instead of coefficient-by-coefficient. **Reed–Solomon parity** via **PARITY_MATRIX** enforces valid Shamir structure inside -[**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation) +[**C2**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/sk_share_computation) without reconstructing secrets in the clear. **CRT / RNS** arithmetic shows up everywhere in BFV, with explicit reconstruction where the statement needs a single ring element—notably in [**C7**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/decrypted_shares_aggregation). diff --git a/docs/pages/noir-circuits.mdx b/docs/pages/noir-circuits.mdx index 0b59363f34..1b128cdf22 100644 --- a/docs/pages/noir-circuits.mdx +++ b/docs/pages/noir-circuits.mdx @@ -33,13 +33,17 @@ circuits/ │ ├── config/ # Pre-deployment preset consistency (`config` circuit) │ ├── dkg/ # C0, C2 pipeline, C3, C4 │ ├── threshold/ # C1, C5, user_data_encryption*, C6, C7 -│ └── recursive_aggregation/ # fold/ + wrapper/{dkg,threshold}/ +│ └── recursive_aggregation/ # AB combiners (c2ab/c3ab/c4ab_fold), per-step +│ # folds (c3_fold[_kernel], c6_fold[_kernel]), +│ # per-node node_fold + cross-node nodes_fold +│ # [_kernel], dkg_aggregator, decryption_aggregator └── benchmarks/ # Timing scripts and reports ``` Binary packages map to **C0–C7** and operational phases as on the [Cryptography](./cryptography) -page (**Phases & Circuits** section). Large statements (especially **C2**) use **inner** proofs plus -**wrapper** + **fold** under `recursive_aggregation/`. +page (**Phases & Circuits** section). Large statements (especially **C2**) use **inner** proofs that +are then combined inside `recursive_aggregation/` (AB combiners → per-step folds → `node_fold` per +party → `nodes_fold` across parties → `dkg_aggregator` / `decryption_aggregator`). ## Using `circuits/lib` from another Noir project diff --git a/docs/pages/tutorials/deploy-to-testnet.mdx b/docs/pages/tutorials/deploy-to-testnet.mdx index 8923c9a871..6954938a13 100644 --- a/docs/pages/tutorials/deploy-to-testnet.mdx +++ b/docs/pages/tutorials/deploy-to-testnet.mdx @@ -136,8 +136,9 @@ const hash = await sdk.requestE3({ }) ``` -Secure parameters use polynomials of larger degrees (N=8192 vs N=512) which makes proof generation slower -but provides real cryptographic security. Expect proof generation to take longer than in local dev. +Secure parameters use polynomials of larger degrees (N=8192 vs N=512) which makes proof generation +slower but provides real cryptographic security. Expect proof generation to take longer than in +local dev. --- diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index 5beead4481..939688c014 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -3,23 +3,23 @@ chains: rpc_url: ws://localhost:8545 contracts: e3_program: - address: "0x4c5859f0F772848b2D91F1D83E2Fe57935348029" + address: "0x809d550fca64d94Bd9F66E60752A544199cfAC3D" deploy_block: 31 enclave: address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" - deploy_block: 13 + deploy_block: 14 ciphernode_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" - deploy_block: 9 + deploy_block: 10 bonding_registry: address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" - deploy_block: 10 + deploy_block: 11 slashing_manager: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" - deploy_block: 8 + deploy_block: 9 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - deploy_block: 4 + deploy_block: 5 program: dev: true # risc0: diff --git a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json index ab3da643bd..22986984e8 100644 --- a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json +++ b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json @@ -151,21 +151,21 @@ }, "localhost": { "PoseidonT3": { - "blockNumber": 3, + "blockNumber": 4, "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "MockUSDC": { "constructorArgs": { "initialSupply": "1000000" }, - "blockNumber": 4, + "blockNumber": 5, "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" }, "EnclaveToken": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 5, + "blockNumber": 6, "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" }, "EnclaveTicketToken": { @@ -174,14 +174,14 @@ "registry": "0x0000000000000000000000000000000000000001", "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 7, + "blockNumber": 8, "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" }, "SlashingManager": { "constructorArgs": { "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 8, + "blockNumber": 9, "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" }, "CiphernodeRegistryOwnable": { @@ -196,7 +196,7 @@ "proxyAdminAddress": "0x9bd03768a7DCc129555dE410FF8E85528A4F88b5", "implementationAddress": "0x0165878A594ca255338adfa4d48449f69242Eb8F" }, - "blockNumber": 9, + "blockNumber": 10, "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" }, "BondingRegistry": { @@ -218,7 +218,7 @@ "proxyAdminAddress": "0x8aCd85898458400f7Db866d53FCFF6f0D49741FF", "implementationAddress": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" }, - "blockNumber": 10, + "blockNumber": 11, "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" }, "Enclave": { @@ -238,7 +238,7 @@ "proxyAdminAddress": "0x8dAF17A20c9DBA35f005b6324F493785D239719d", "implementationAddress": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" }, - "blockNumber": 13, + "blockNumber": 14, "address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, "E3RefundManager": { @@ -254,69 +254,65 @@ "proxyAdminAddress": "0x32467b43BFa67273FC7dDda0999Ee9A12F2AaA08", "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" }, - "blockNumber": 15, + "blockNumber": 16, "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" }, "MockComputeProvider": { - "blockNumber": 17, + "blockNumber": 18, "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" }, "MockDecryptionVerifier": { - "blockNumber": 18, + "blockNumber": 19, "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9" }, "MockPkVerifier": { - "blockNumber": 19, + "blockNumber": 20, "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" }, "MockE3Program": { - "blockNumber": 20, + "blockNumber": 21, "address": "0x851356ae760d987E095750cCeb3bC6014560891C" }, "ZKTranscriptLib": { - "blockNumber": 22, - "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778" - }, - "RecursiveAggregationFoldVerifier": { "blockNumber": 23, - "address": "0x998abeb3E57409262aE5b751f60747921B33613E" + "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778" }, - "ThresholdDecryptedSharesAggregationVerifier": { + "DecryptionAggregatorVerifier": { "blockNumber": 24, - "address": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49" + "address": "0x998abeb3E57409262aE5b751f60747921B33613E" }, - "ThresholdPkAggregationVerifier": { + "DkgAggregatorVerifier": { "blockNumber": 25, - "address": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528" + "address": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49" }, "BfvDecryptionVerifier": { "blockNumber": 26, - "address": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf" + "address": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528" }, "BfvPkVerifier": { "blockNumber": 28, - "address": "0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf" + "address": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF" }, "MockRISC0Verifier": { - "address": "0x5eb3Bc0a489C5A8288765d2336659EbCA68FCd00", + "address": "0x9d4454B023096f34B160D6B654540c56A1F81688", "blockNumber": 30 }, "HonkVerifier": { - "address": "0x809d550fca64d94Bd9F66E60752A544199cfAC3D", + "address": "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570", "blockNumber": 31 }, "CRISPProgram": { - "address": "0x4c5859f0F772848b2D91F1D83E2Fe57935348029", + "address": "0x809d550fca64d94Bd9F66E60752A544199cfAC3D", "blockNumber": 31, "constructorArgs": { "enclave": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", - "verifierAddress": "0x5eb3Bc0a489C5A8288765d2336659EbCA68FCd00", - "honkVerifierAddress": "0x809d550fca64d94Bd9F66E60752A544199cfAC3D", + "verifierAddress": "0x9d4454B023096f34B160D6B654540c56A1F81688", + "honkVerifierAddress": "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570", "imageId": "0x23734b77b0f76e85623a88d7a82f24c34c94834f2501964ea123b7a2027013a2" } }, "MockVotingToken": { - "address": "0x5f3f1dBD7B74C6B46e8c44f98792A1dAf8d69154", + "address": "0x1291Be112d480055DaFd8a610b7d1e203891C274", "blockNumber": 33 } } diff --git a/examples/CRISP/packages/crisp-contracts/hardhat.config.ts b/examples/CRISP/packages/crisp-contracts/hardhat.config.ts index bed7a6fbcb..f7c903260e 100644 --- a/examples/CRISP/packages/crisp-contracts/hardhat.config.ts +++ b/examples/CRISP/packages/crisp-contracts/hardhat.config.ts @@ -144,9 +144,8 @@ const config: HardhatUserConfig = { '@enclave-e3/contracts/contracts/test/MockStableToken.sol', '@enclave-e3/contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol', '@enclave-e3/contracts/contracts/verifiers/bfv/BfvPkVerifier.sol', - '@enclave-e3/contracts/contracts/verifiers/bfv/honk/RecursiveAggregationFoldVerifier.sol', - '@enclave-e3/contracts/contracts/verifiers/bfv/honk/ThresholdDecryptedSharesAggregationVerifier.sol', - '@enclave-e3/contracts/contracts/verifiers/bfv/honk/ThresholdPkAggregationVerifier.sol', + '@enclave-e3/contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol', + '@enclave-e3/contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol', ], settings: { optimizer: { diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index a4f7a24c36..e3d3929bfb 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -15,7 +15,7 @@ CRON_API_KEY=1234567890 # Based on Default Anvil Deployments (Only for testing) ENCLAVE_ADDRESS=0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e FEE_TOKEN_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 -E3_PROGRAM_ADDRESS=0x4c5859f0F772848b2D91F1D83E2Fe57935348029 +E3_PROGRAM_ADDRESS=0x809d550fca64d94Bd9F66E60752A544199cfAC3D CIPHERNODE_REGISTRY_ADDRESS=0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 # E3 Config diff --git a/examples/CRISP/server/src/cli/commands.rs b/examples/CRISP/server/src/cli/commands.rs index a5f0461a82..e0dfdb16d3 100644 --- a/examples/CRISP/server/src/cli/commands.rs +++ b/examples/CRISP/server/src/cli/commands.rs @@ -18,7 +18,7 @@ use alloy::primitives::{Address, Bytes, U256}; use alloy::providers::{Provider, ProviderBuilder}; use alloy::sol_types::SolValue; use crisp::config::CONFIG; -use e3_fhe_params::{build_bfv_params_from_set_arc, encode_bfv_params}; +use e3_fhe_params::build_bfv_params_from_set_arc; use e3_sdk::evm_helpers::contracts::{CommitteeSize, EnclaveContract, EnclaveRead, EnclaveWrite}; use fhe::bfv::{BfvParameters, Ciphertext, Encoding, Plaintext, PublicKey, SecretKey}; use fhe_traits::{ @@ -202,7 +202,7 @@ pub async fn initialize_crisp_round( // Recompute the current timestamp to ensure it's as up-to-date as possible before sending the transaction, // since there are multiple steps (fee quote, token approval) that could take time. - let mut current_timestamp = get_current_timestamp().await?; + let current_timestamp = get_current_timestamp().await?; // Buffer so tx can mine before window opens; end = start + duration so voting window equals e3_duration let window_start = current_timestamp + 20; let input_window: [U256; 2] = [ @@ -312,7 +312,6 @@ pub async fn decrypt_and_publish_result( U256::from(input_crisp_id), Bytes::from(votes.to_be_bytes()), proof, - Bytes::new(), ) .await?; info!("Vote broadcast. TxHash: {:?}", res.transaction_hash); diff --git a/package.json b/package.json index cd916302b1..bf1be5b1fb 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "scripts": { "bump:versions": "tsx scripts/bump-versions.ts", "build:circuits": "tsx scripts/build-circuits.ts", - "generate:verifiers": "tsx scripts/generate-verifiers.ts --circuits pk_aggregation,decrypted_shares_aggregation,fold", + "generate:verifiers": "tsx scripts/generate-verifiers.ts --circuits dkg_aggregator,decryption_aggregator", "store:circuits": "tsx scripts/circuit-artifacts.ts", "clean": "tsx scripts/clean.ts", "compile": "pnpm build:ts && pnpm rust:build", diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index a09ba7ccc5..043a55c14b 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -435,6 +435,11 @@ "name": "PlaintextOutputAlreadyPublished", "type": "error" }, + { + "inputs": [], + "name": "ProofRequired", + "type": "error" + }, { "inputs": [ { @@ -2079,11 +2084,6 @@ "internalType": "bytes", "name": "proof", "type": "bytes" - }, - { - "internalType": "bytes", - "name": "foldProof", - "type": "bytes" } ], "name": "publishPlaintextOutput", @@ -2539,11 +2539,11 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b615a13806100d65f395ff3fe608060405234801561000f575f5ffd5b50600436106102ce575f3560e01c80639117173c11610182578063cb649617116100e0578063f0691cba1161008f578063f0691cba14610886578063f2fde38b14610899578063f3ceba3a146108ac578063f81b8ef6146108cd578063fad8e111146108e0578063fbdb3237146108f3578063fd2f3d011461091b575f5ffd5b8063cb649617146107ef578063cbd16872146107f8578063cf0f34c41461080b578063cfbdc98d1461081e578063d8afed3e1461084d578063e59e469514610860578063ea71aa5714610873575f5ffd5b8063a87f4ab91161013c578063a87f4ab914610607578063ac3d2f4214610759578063b27392d514610781578063bb2d1b8214610794578063bff232c1146107a7578063c1ab0f1f146107ba578063c4ccafa2146107cd575f5ffd5b80639117173c146105625780639231238614610575578063929a8faf1461058857806399c6679d146105a95780639c8570c8146105d15780639e57b934146105f4575f5ffd5b80635d1684181161022f57806381476ec2116101e957806381476ec2146104e7578063830d7181146104fa578063858142431461050d5780638da5cb5b146105205780638dcdd86b146105285780638e5ce3ad1461053a57806390173a411461054d575f5ffd5b80635d1684181461047d578063647846a51461049d5780636db5c8fd146104b0578063715018a6146104b95780637c8c3b4d146104c15780637cfa9d74146104d4575f5ffd5b806336c5d38a1161028b57806336c5d38a1461039b5780634017daf0146103ca578063406ed35c146103f75780634147a360146104175780634d600e5d146104445780634e92ec63146104575780634fc772641461046a575f5ffd5b806302a3a9c9146102d25780630ef81b2f146102e757806310bc62811461032557806311bd61d91461034d57806315cce224146103755780631ba7294514610388575b5f5ffd5b6102e56102e03660046145e2565b61092e565b005b61030f6102f5366004614604565b5f908152600960205260409020546001600160a01b031690565b60405161031c9190614628565b60405180910390f35b61030f610333366004614604565b60096020525f90815260409020546001600160a01b031681565b61036061035b36600461464f565b6109da565b60405163ffffffff909116815260200161031c565b6102e56103833660046145e2565b610a16565b6102e5610396366004614687565b610abb565b6103bd6103a9366004614604565b5f908152600f602052604090205460ff1690565b60405161031c91906146c9565b6103dd6103d8366004614604565b610acf565b60405161031c9e9d9c9b9a99989796959493929190614715565b61040a610405366004614604565b610c7a565b60405161031c91906148ef565b610436610425366004614604565b600c6020525f908152604090205481565b60405190815260200161031c565b6102e561045236600461490c565b610ef7565b6102e5610465366004614604565b611134565b6102e56104783660046145e2565b6111c3565b61049061048b3660046149a2565b611256565b60405161031c91906149bb565b60045461030f906001600160a01b031681565b61043660055481565b6102e56112ed565b6102e56104cf3660046149cd565b611300565b6102e56104e2366004614604565b611389565b6102e56104f53660046149fb565b611488565b6102e5610508366004614a58565b61157c565b60015461030f906001600160a01b031681565b61030f611688565b5f5461030f906001600160a01b031681565b60035461030f906001600160a01b031681565b6105556116b6565b60405161031c9190614aa6565b6102e5610570366004614604565b6116fc565b610555610583366004614604565b61186a565b61059b610596366004614604565b6118c3565b60405161031c929190614ac7565b61030f6105b7366004614604565b5f908152601060205260409020546001600160a01b031690565b6105e46105df366004614add565b6118ea565b604051901515815260200161031c565b610436610602366004614b54565b611b82565b61074c604080516101e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915250604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a0840152640100000000909104166101c082015290565b60405161031c9190614b8b565b61030f610767366004614604565b5f908152600a60205260409020546001600160a01b031690565b6105e461078f366004614c98565b612157565b6102e56107a2366004614d3d565b612395565b6102e56107b53660046145e2565b612451565b6102e56107c83660046149fb565b6124f8565b6105e46107db3660046145e2565b60076020525f908152604090205460ff1681565b61043660065481565b6102e56108063660046149cd565b6125b5565b6102e5610819366004614604565b61266f565b61084061082c366004614604565b5f908152600d602052604090205460ff1690565b60405161031c9190614d77565b6102e561085b366004614d85565b6126ac565b6102e561086e3660046145e2565b612939565b6102e5610881366004614d9f565b6129d3565b60025461030f906001600160a01b031681565b6102e56108a73660046145e2565b612c80565b6108bf6108ba366004614b54565b612cba565b60405161031c929190614dd6565b6103bd6108db366004614604565b61359b565b6102e56108ee3660046145e2565b613735565b61030f610901366004614604565b600a6020525f90815260409020546001600160a01b031681565b6102e56109293660046145e2565b6137cd565b61093661385c565b6001600160a01b0381166109915760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964204533526566756e644d616e6167657220616464726573730060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040517f9557d04c1c0b16f93f13b69aed23b3b6ab935bff3c53ac81d17896d3583542ed905f90a250565b6012602052815f5260405f2081600281106109f3575f80fd5b60089182820401919006600402915091509054906101000a900463ffffffff1681565b610a1e61385c565b6001600160a01b03811615801590610a4457506004546001600160a01b03828116911614155b8190610a645760405163eddf07f560e01b81526004016109889190614628565b50600480546001600160a01b0319166001600160a01b0383161790556040517f722ff84c1234b2482061def5c82c6b5080c117b3cbb69d686844a051e4b8e7f390610ab0908390614628565b60405180910390a150565b610ac361385c565b610acc8161388e565b50565b60086020525f9081526040902080546001820154600283015460058401546006850154600786018054959660ff95861696949593946001600160a01b03841694600160a01b90940490931692909190610b2790614df6565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5390614df6565b8015610b9e5780601f10610b7557610100808354040283529160200191610b9e565b820191905f5260205f20905b815481529060010190602001808311610b8157829003601f168201915b50505060088401546009850154600a860154600b870154600c8801805497986001600160a01b03958616989490951696509194509291610bdd90614df6565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0990614df6565b8015610c545780601f10610c2b57610100808354040283529160200191610c54565b820191905f5260205f20905b815481529060010190602001808311610c3757829003601f168201915b505050600d90930154919250506001600160a01b0381169060ff600160a01b909104168e565b610c82614450565b5f8281526008602090815260409182902082516101e08101909352805483526001810154909183019060ff166003811115610cbf57610cbf6146a1565b6003811115610cd057610cd06146a1565b8152600282810154602083015260408051808201808352919093019291600385019182845b815481526020019060010190808311610cf55750505091835250506005820154602082015260068201546001600160a01b0381166040830152600160a01b900460ff166060820152600782018054608090920191610d5290614df6565b80601f0160208091040260200160405190810160405280929190818152602001828054610d7e90614df6565b8015610dc95780601f10610da057610100808354040283529160200191610dc9565b820191905f5260205f20905b815481529060010190602001808311610dac57829003601f168201915b505050918352505060088201546001600160a01b0390811660208301526009830154166040820152600a8201546060820152600b8201546080820152600c8201805460a090920191610e1a90614df6565b80601f0160208091040260200160405190810160405280929190818152602001828054610e4690614df6565b8015610e915780601f10610e6857610100808354040283529160200191610e91565b820191905f5260205f20905b815481529060010190602001808311610e7457829003601f168201915b5050509183525050600d91909101546001600160a01b038082166020840152600160a01b90910460ff16151560409092019190915260a0820151919250839116610ef15760405163cd6f4a4f60e01b815260040161098891815260200190565b50919050565b5f610f0061394b565b805490915060ff600160401b82041615906001600160401b03165f81158015610f265750825b90505f826001600160401b03166001148015610f415750303b155b905081158015610f4f575080155b15610f6d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f9757845460ff60401b1916600160401b1785555b610fa033613975565b610fa98761266f565b610fb28b613735565b610fbb8a612939565b610fc48961092e565b610fcd88610a16565b610fd68661388e565b604080516101e08101825261c3508082526161a86020808401829052611388948401859052601460608501819052620249f0608086018190526207a12060a087018190526107d060c088018190525f60e089018190526103e86101008a015261012089018190526109c46101408a018190526101608a018b90526101808a01526101a089018190526101c090980197909752601895909555601993909355601a95909555601b94909455601c55601d55601e55601f80546001600160f01b031916690138827101388000007d60a31b179055805467ffffffffffffffff191690556110bf611688565b6001600160a01b03168c6001600160a01b0316146110e0576110e08c612c80565b831561112657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050565b61113c61385c565b5f8181526009602052604090205481906001600160a01b0316611175576040516381c4951960e01b815260040161098891815260200190565b505f818152600960205260409081902080546001600160a01b0319169055517f104eb329a192aef26eddea07c2af5ad2587792e62b37ed4045b6ba59bc5540fc90610ab09083815260200190565b6111cb61385c565b6001600160a01b0381165f90815260076020526040902054819060ff16611206576040516321ac7c5f60e01b81526004016109889190614628565b506001600160a01b0381165f9081526007602052604090819020805460ff19169055517f56070b80bd617fcd2f7a284861edb488830a38f9dedcd77b2cb2f4eac17743e790610ab0908390614628565b600b6020525f90815260409020805461126e90614df6565b80601f016020809104026020016040519081016040528092919081815260200182805461129a90614df6565b80156112e55780601f106112bc576101008083540402835291602001916112e5565b820191905f5260205f20905b8154815290600101906020018083116112c857829003601f168201915b505050505081565b6112f561385c565b6112fe5f613986565b565b61130861385c565b6001600160a01b0381161580159061133957505f828152600a60205260409020546001600160a01b03828116911614155b829061135b576040516381c4951960e01b815260040161098891815260200190565b505f918252600a602052604090912080546001600160a01b0319166001600160a01b03909216919091179055565b5f546001600160a01b031633146113b35760405163b56831db60e01b815260040160405180910390fd5b5f818152600d602052604090205460ff1660018160068111156113d8576113d86146a1565b146113fd57816001826040516337e1404160e01b815260040161098893929190614e28565b5f828152600d60205260409020805460ff191660021790556015546114229042614e5d565b5f838152600e602052604080822092909255905183917fc44405af9078047712501f519e1fb900c2896c62b488336f84529c72ae16e6f191a2815f5160206159e75f395f51905f526001600260405161147c929190614e70565b60405180910390a25050565b5f546001600160a01b031633146114b25760405163b56831db60e01b815260040160405180910390fd5b5f828152600860209081526040808320600d9092529091205460ff1660028160068111156114e2576114e26146a1565b1461150757836002826040516337e1404160e01b815260040161098893929190614e28565b5f848152600d6020526040808220805460ff19166003179055600a84018590555185917f11df18edb9bc9cd90a79068e0e208b630202148643d797d6150e7bacb733e63c91a2835f5160206159e75f395f51905f526002600360405161156e929190614e70565b60405180910390a250505050565b61158461385c565b806115c05760405162461bcd60e51b815260206004820152600c60248201526b456d70747920706172616d7360a01b6044820152606401610988565b60ff83165f908152600b6020526040902080546115dc90614df6565b15905061162b5760405162461bcd60e51b815260206004820152601b60248201527f506172616d53657420616c7265616479207265676973746572656400000000006044820152606401610988565b60ff83165f908152600b60205260409020611647828483614efe565b507f6e4a4ea7f38fc775e616080b155744337e6216848e886a69c918b4ab84da219583838360405161167b93929190614fda565b60405180910390a1505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6116d760405180606001604052805f81526020015f81526020015f81525090565b5060408051606081018252601554815260165460208201526017549181019190915290565b5f818152600d602052604090205460ff166006816006811115611721576117216146a1565b14829061174457604051637cb2d48360e11b815260040161098891815260200190565b505f828152600c60205260409020548281611775576040516345ba89d560e11b815260040161098891815260200190565b505f838152600c6020526040812081905561178f846139f6565b5f858152601160205260409020546002549192506001600160a01b03908116916117bc9183911685613ae3565b60025460405163da19b69760e01b81526001600160a01b039091169063da19b697906117f2908890879087908790600401615042565b5f604051808303815f87803b158015611809575f5ffd5b505af115801561181b573d5f5f3e3d5ffd5b50505050847f5297818f48a66292b8b3e2caab83eec531b669bb20807fd38cf006adb2a0731784845160405161185b929190918252602082015260400190565b60405180910390a25050505050565b61188b60405180606001604052805f81526020015f81526020015f81525090565b505f908152600e6020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b5f818152600d6020526040812054819060ff166118e08482613b40565b9250925050915091565b5f5f6118f587610c7a565b5f888152600d602052604090205490915060ff16600381600681111561191d5761191d6146a1565b1488600383909192611945576040516337e1404160e01b815260040161098893929190614e28565b5050505f888152600e60209081526040918290208251606081018452815481526001820154928101839052600290910154928101929092528990428110156119a9576040516308f3034360e31b815260048101929092526024820152604401610988565b50506060830151602001518990428111156119e05760405163017e35e560e71b815260048101929092526024820152604401610988565b5050610160830151899015611a0b57604051637eb9cea960e11b815260040161098891815260200190565b505f8888604051611a1d92919061508d565b60408051918290039091205f8c815260086020908152838220600b01839055600d905291909120805460ff19166004179055601754909150611a5f9042614e5d565b5f8b8152600e6020526040908190206002019190915560a08501519051632f0e1bbf60e01b81526001600160a01b0390911690632f0e1bbf90611aac908d9085908c908c9060040161509c565b6020604051808303815f875af1158015611ac8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611aec91906150c8565b9450888886611b1057604051632f9f8ab960e01b81526004016109889291906150e3565b5050897f7cc27e4a5626cbc4f8ba1a927b0448de55e6a114bc87660331270c5109ade0718a8a604051611b449291906150e3565b60405180910390a2895f5160206159e75f395f51905f5260036004604051611b6d929190614e70565b60405180910390a25050505095945050505050565b5f80600b81611b9760a08601608087016149a2565b60ff1660ff1681526020019081526020015f208054611bb590614df6565b905011611bd45760405162461bcd60e51b8152600401610988906150f6565b5f601281611be5602086018661512d565b6003811115611bf657611bf66146a1565b6003811115611c0757611c076146a1565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411611c2e579050505050505090505f81600160028110611c8557611c85615079565b602002015163ffffffff1611835f016020810190611ca3919061512d565b90611cc25760405163286c068d60e11b81526004016109889190615146565b506020808201518251604080516101e081018252601854815260195481860152601a5491810191909152601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e0830152600160a01b810461ffff908116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152925463ffffffff8181166101a0860181905264010000000090920481166101c086015292831693919092169115611ded576101a081015163ffffffff16846001602002015163ffffffff161015865f016020810190611dcc919061512d565b90611deb5760405163010b971d60e31b81526004016109889190615146565b505b6101c081015163ffffffff1615611e3c576101c081015184519063ffffffff9081169082161015611e3a57604051630a4b6b6360e11b815263ffffffff9091166004820152602401610988565b505b60408601356020870135811015611e695760405163174b5a0760e21b815260040161098891815260200190565b506101808101516017545f9161271091611e879161ffff1690615154565b611e91919061516b565b61271061ffff1683610160015161ffff16601560010154611eb29190615154565b611ebc919061516b565b61271061ffff1684610140015161ffff1660155f0154611edc9190615154565b611ee6919061516b565b5f5460408051634f87c3a560e11b8152815160208e81013594938f0135936001600160a01b031692639f0f874a92600480830193928290030181865afa158015611f32573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f56919061518a565b611f609190614e5d565b611f6a91906151a1565b611f749190614e5d565b611f7e9190614e5d565b611f889190614e5d565b90505f611f966001866151a1565b611fa1906002615154565b611fac906002615154565b611fb7906006614e5d565b90505f85845f0151611fc99190615154565b905081868560200151611fdc9190615154565b611fe69190615154565b611ff09082614e5d565b905060018611156120385760026120086001886151a1565b6120129088615154565b85604001516120219190615154565b61202b919061516b565b6120359082614e5d565b90505b81868560c001516120499190615154565b6120539190615154565b61205d9082614e5d565b9050828685606001516120709190615154565b61207a9190615154565b6120849082614e5d565b90508484608001516120969190615154565b6120a09082614e5d565b905060018511156120e85760026120b86001876151a1565b6120c29087615154565b85604001516120d19190615154565b6120db919061516b565b6120e59082614e5d565b90505b60a08401516120f79082614e5d565b610100850151909150612710906121129061ffff1682614e5d565b61211c9083615154565b612126919061516b565b9750878061214a57604051638c4fcd9360e01b815260040161098891815260200190565b5050505050505050919050565b5f5f61216289610c7a565b5f8a8152600d602052604090205490915060ff16600481600681111561218a5761218a6146a1565b148a6004839091926121b2576040516337e1404160e01b815260040161098893929190614e28565b5050505f8a8152600e602090815260409182902082516060810184528154815260018201549281019290925260020154918101829052908b9042811015612215576040516308f3034360e31b815260048101929092526024820152604401610988565b50505f8b8152600860205260409020600c016122328a8c83614efe565b505f8b8152600d60205260409020805460ff191660051790556101c083015115612312578261010001516001600160a01b0316635bf48e3a8b8b60405161227a92919061508d565b6040519081900381206001600160e01b031960e084901b1682526122a8918c908c908c908c906004016151b4565b602060405180830381865afa1580156122c3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122e791906150c8565b935089898561230b57604051632f9f8ab960e01b81526004016109889291906150e3565b5050612317565b600193505b6123208b613cc8565b8a7f3a140076c461ebc41d74833ae0ee8bbc8079a135a63392098cd381e84350b69b8b8b8b8b60405161235694939291906151ec565b60405180910390a28a5f5160206159e75f395f51905f526004600560405161237f929190614e70565b60405180910390a2505050979650505050505050565b5f546001600160a01b03163314806123b757506003546001600160a01b031633145b6123d457604051639e75a8b560e01b815260040160405180910390fd5b5f8160ff161180156123ea5750600d60ff821611155b61242f5760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b2103330b4b63ab932903932b0b9b7b760511b6044820152606401610988565b61244d828260ff16600d811115612448576124486146a1565b614134565b5050565b61245961385c565b6001600160a01b0381166124af5760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420536c617368696e674d616e616765722061646472657373006044820152606401610988565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6003546001600160a01b03163314612523576040516357d6948d60e11b815260040160405180910390fd5b60025460405163c1ab0f1f60e01b815260048101849052602481018390526001600160a01b039091169063c1ab0f1f906044015f604051808303815f87803b15801561256d575f5ffd5b505af115801561257f573d5f5f3e3d5ffd5b50505050817f4f41a3b0a032ebcae925f2ace77d507435840ca4b2dbaffdd7723fa8d72ee5428260405161147c91815260200190565b6125bd61385c565b6001600160a01b038116158015906125ee57505f828152600960205260409020546001600160a01b03828116911614155b8290612610576040516381c4951960e01b815260040161098891815260200190565b505f8281526009602090815260409182902080546001600160a01b0319166001600160a01b03851617905590518381527ff4041a3f914dac3bc9bf5f003ba41f28dbb84abe42f4e07c76266f5c8ceecb69910160405180910390a15050565b61267761385c565b60058190556040518181527fba0716ba1ee2ea8ecc4c64119b4537cdb42a99d82acf92af5b87607b8b52355290602001610ab0565b6126b461385c565b6127106126c961012083016101008401615237565b61ffff1611156126e161012083016101008401615237565b90612706576040516301027fc160e21b815261ffff9091166004820152602401610988565b5061271061271c61014083016101208401615237565b61ffff16111561273461014083016101208401615237565b90612759576040516301027fc160e21b815261ffff9091166004820152602401610988565b5061271061276f61016083016101408401615237565b61ffff16111561278761016083016101408401615237565b906127ac57604051633239953960e01b815261ffff9091166004820152602401610988565b506127106127c261018083016101608401615237565b61ffff1611156127da61018083016101608401615237565b906127ff57604051633239953960e01b815261ffff9091166004820152602401610988565b506127106128156101a083016101808401615237565b61ffff16111561282d6101a083016101808401615237565b9061285257604051633239953960e01b815261ffff9091166004820152602401610988565b5061286561014082016101208301615237565b61ffff16158061288e57505f612882610100830160e084016145e2565b6001600160a01b031614155b6128ab5760405163015f92ff60e51b815260040160405180910390fd5b6128bd6101e082016101c0830161526e565b63ffffffff166128d56101c083016101a0840161526e565b63ffffffff1610156128fa576040516392f55c6560e01b815260040160405180910390fd5b80601861290782826152ad565b9050507fbf3951313e980027eb48ce363fdb707286195ec6a0f802ac153927cf929c3fc681604051610ab0919061546b565b61294161385c565b6001600160a01b0381161580159061296757506001546001600160a01b03828116911614155b8190612987576040516320252f0b60e01b81526004016109889190614628565b50600180546001600160a01b0319166001600160a01b0383161790556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790610ab0908390614628565b6129db61385c565b6129e8602082018261526e565b63ffffffff166129fe604083016020840161526e565b63ffffffff1610158015612a2357505f612a1b602083018361526e565b63ffffffff16115b612a4057604051634564ab9b60e01b815260040160405180910390fd5b604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a08401819052640100000000909204166101c083015215612b79576101a081015163ffffffff16612b29604084016020850161526e565b63ffffffff161015612b41604084016020850161526e565b826101a001519091612b7657604051633ccc4c2160e21b815263ffffffff928316600482015291166024820152604401610988565b50505b6101c081015163ffffffff1615612bf0576101c081015163ffffffff16612ba3602084018461526e565b63ffffffff161015612bb8602084018461526e565b826101c001519091612bed5760405163156c4e5b60e11b815263ffffffff928316600482015291166024820152604401610988565b50505b8160125f856003811115612c0657612c066146a1565b6003811115612c1757612c176146a1565b815260208101919091526040015f20612c319160026144cd565b50826003811115612c4457612c446146a1565b7f8b56fae526eee054f0849759a99fc7d4ff3823824ebf097a56f7d78adb6b34fa83604051612c739190615575565b60405180910390a2505050565b612c8861385c565b6001600160a01b038116612cb1575f604051631e4fbdf760e01b81526004016109889190614628565b610acc81613986565b5f612cc3614450565b5f601281612cd4602087018761512d565b6003811115612ce557612ce56146a1565b6003811115612cf657612cf66146a1565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612d1d579050505050505090505f81600160028110612d7457612d74615079565b602002015163ffffffff1611845f016020810190612d92919061512d565b90612db15760405163286c068d60e11b81526004016109889190615146565b50602084013542811015612ddb57604051630b99e87960e01b815260040161098891815260200190565b5060408401356020850135811015612e095760405163174b5a0760e21b815260040161098891815260200190565b506017546016545f9190612e214260408901356151a1565b612e2b9190614e5d565b612e359190614e5d565b905060055481108190612e5e576040516313b783af60e21b815260040161098891815260200190565b5060075f612e7260808801606089016145e2565b6001600160a01b0316815260208101919091526040015f205460ff16612e9e60808701606088016145e2565b90612ebd5760405163295a6a6f60e11b81526004016109889190614628565b505f612ec886611b82565b60068054965090915085905f612edd836155b5565b9091555050604080514460208201529081018690525f9060600160408051601f1981840301815291815281516020928301205f898152600c84528281208690556004546011855283822080546001600160a01b03199081166001600160a01b0393841617909155601f805460138852868520805461ffff191661ffff600160b01b909304929092169190911790555460148752858420805483169190931617909155600d8552838220805460ff191660011790556010909452829020805433941693909317909255601654919250612fb89190890135614e5d565b5f878152600e6020908152604090912060010191909155818652612fde9088018861512d565b85602001906003811115612ff457612ff46146a1565b90816003811115613007576130076146a1565b905250436040808701919091528051808201825290602089019060029083908390808284375f92019190915250505060608087019190915261304f90608089019089016145e2565b6001600160a01b031660a080870191909152613070908801608089016149a2565b60ff1660c080870191909152613088908801886155cd565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060e0808701919091526130d590610100890190890161560f565b15156101c08601525f610140860181905261016086018190526040805160208101909152908152610180860152336101a08601819052600454613125916001600160a01b0390911690308561428f565b5f600b8161313960a08b0160808c016149a2565b60ff1660ff1681526020019081526020015f20805461315790614df6565b80601f016020809104026020016040519081016040528092919081815260200182805461318390614df6565b80156131ce5780601f106131a5576101008083540402835291602001916131ce565b820191905f5260205f20905b8154815290600101906020018083116131b157829003601f168201915b505050505090505f8151116131f55760405162461bcd60e51b8152600401610988906150f6565b5f61320660808a0160608b016145e2565b6001600160a01b031663fefd9a8b89858561322460a08f018f6155cd565b8f8060c0019061323491906155cd565b6040518863ffffffff1660e01b8152600401613256979695949392919061562a565b6020604051808303815f875af1158015613272573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613296919061518a565b5f818152600960205260409020549091506001600160a01b031681816132d2576040516381c4951960e01b815260040161098891815260200190565b505f828152600a60205260409020546001600160a01b0316828161330c576040516381c4951960e01b815260040161098891815260200190565b50608089018390526001600160a01b038083166101008b015281166101208a01525f8a81526008602090815260409091208a518155908a0151600180830180548d94939260ff19919091169083600381111561336a5761336a6146a1565b02179055506040820151816002015560608201518160030190600261339092919061456e565b506080820151600582015560a082015160068201805460c085015160ff16600160a01b026001600160a81b03199091166001600160a01b039093169290921791909117905560e082015160078201906133e9908261567e565b506101008201516008820180546001600160a01b039283166001600160a01b031991821617909155610120840151600984018054919093169116179055610140820151600a820155610160820151600b820155610180820151600c820190613451908261567e565b506101a0820151600d90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091555f5460405163291a691b60e01b815291169063291a691b906134b7908d9089908d90600401615733565b6020604051808303815f875af11580156134d3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134f791906150c8565b61351457604051630d8dbe2560e01b815260040160405180910390fd5b61352460808c0160608d016145e2565b6001600160a01b03167f5090c9764b5cd13df7afc0013f733dfbe6eaf1b6ddc22a5e291fa387efd4c15e8b8b60405161355e929190614dd6565b60405180910390a2895f5160206159e75f395f51905f525f6001604051613586929190614e70565b60405180910390a25050505050505050915091565b5f818152600d602052604081205460ff16818160068111156135bf576135bf6146a1565b036135e457826001826040516337e1404160e01b815260040161098893929190614e28565b60058160068111156135f8576135f86146a1565b036136195760405163462c7bed60e01b815260048101849052602401610988565b600681600681111561362d5761362d6146a1565b0361364e57604051633de16e3560e11b815260048101849052602401610988565b5f6136598483613b40565b935090508061367e57604051639f65d93560e01b815260048101859052602401610988565b5f848152600d6020526040902080546006919060ff191660018302179055505f848152600f60205260409020805484919060ff1916600183600d8111156136c7576136c76146a1565b0217905550835f5160206159e75f395f51905f528360066040516136ec929190614e70565b60405180910390a2837fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb8385604051613726929190615778565b60405180910390a25050919050565b61373d61385c565b6001600160a01b0381161580159061376257505f546001600160a01b03828116911614155b8190613782576040516375ac4eb760e11b81526004016109889190614628565b505f80546001600160a01b0319166001600160a01b0383161790556040517f80052b810d39120cf6c976cca504a21703f585521dc7a41c6d241090e6c579b690610ab0908390614628565b6001600160a01b0381165f90815260076020526040902054819060ff16156138095760405163b29d459560e01b81526004016109889190614628565b506001600160a01b0381165f9081526007602052604090819020805460ff19166001179055517fb8d368517268f297fff00825d67d098763117d061360d31027be5b2e1a59d46790610ab0908390614628565b33613865611688565b6001600160a01b0316146112fe573360405163118cdaa760e01b81526004016109889190614628565b80356138ad5760405163055f269d60e01b815260040160405180910390fd5b5f8160200135116138d15760405163055f269d60e01b815260040160405180910390fd5b5f8160400135116138f55760405163055f269d60e01b815260040160405180910390fd5b80356015819055602080830135601681905560408085013560178190558151948552928401919091528201527f7e86ba16b805e2835af5c5b7aa5a942ced8bcc1fb95a05fbe42dae3862350a1690606001610ab0565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b61397d6142ce565b610acc816142f3565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f818152600f602052604090205460609060ff16600181600d811115613a1e57613a1e6146a1565b1480613a3b5750600281600d811115613a3957613a396146a1565b145b15613a73575f5b604051908082528060200260200182016040528015613a6b578160200160208202803683370190505b509392505050565b5f5460405162beb08960e51b8152600481018590526001600160a01b03909116906317d61120906024015f60405180830381865afa925050508015613ad957506040513d5f823e601f3d908101601f19168201604052613ad6919081019061584f565b60015b613a6b575f613a42565b613b3b83846001600160a01b031663a9059cbb8585604051602401613b09929190615914565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506142fb565b505050565b5f828152600e60209081526040808320815160608101835281548152600182015493810193909352600201548282015282549051632800d82960e01b81526004810186905283929183916001600160a01b0390911690632800d82990602401602060405180830381865afa158015613bba573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bde919061518a565b90506001856006811115613bf457613bf46146a1565b148015613c0057508042115b15613c1357600180935093505050613cc1565b6002856006811115613c2757613c276146a1565b148015613c345750815142115b15613c485760016003935093505050613cc1565b6003856006811115613c5c57613c5c6146a1565b148015613c6c5750816020015142115b15613c805760016006935093505050613cc1565b6004856006811115613c9457613c946146a1565b148015613ca45750816040015142115b15613cb8576001600a935093505050613cc1565b5f5f9350935050505b9250929050565b5f805460405162beb08960e51b8152600481018490526001600160a01b03909116906317d61120906024015f60405180830381865afa158015613d0d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613d34919081019061584f565b5080515f848152600c60209081526040808320805490849055601190925282205493945091926001600160a01b031690829003613dd3576002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613d9f9088908890869060040161592d565b5f604051808303815f87803b158015613db6575f5ffd5b505af1158015613dc8573d5f5f3e3d5ffd5b505050505050505050565b825f03613e74575f858152601060205260409020546001600160a01b03168015613e0b57613e0b6001600160a01b0383168285613ae3565b6002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613e3f9089908990879060040161592d565b5f604051808303815f87803b158015613e56575f5ffd5b505af1158015613e68573d5f5f3e3d5ffd5b50505050505050505050565b5f85815260136020908152604080832054601490925282205461ffff909116906001600160a01b03168115801590613eb457506001600160a01b03811615155b15613ef057612710613eca61ffff841687615154565b613ed4919061516b565b92508215613ef057613ef06001600160a01b0385168285613ae3565b5f613efb84876151a1565b90505f876001600160401b03811115613f1657613f16614e8b565b604051908082528060200260200182016040528015613f3f578160200160208202803683370190505b5090505f613f4d898461516b565b90505f805b8a811015613f8c5782848281518110613f6d57613f6d615079565b6020908102919091010152613f828383614e5d565b9150600101613f52565b505f613f9882866151a1565b90508015613fd5578084613fad60018e6151a1565b81518110613fbd57613fbd615079565b60200260200101818151613fd19190614e5d565b9052505b600154613fef906001600160a01b038b811691168761435e565b60015f9054906101000a90046001600160a01b03166001600160a01b031663dd8c818e8a8e876040518463ffffffff1660e01b81526004016140339392919061598d565b5f604051808303815f87803b15801561404a575f5ffd5b505af115801561405c573d5f5f3e3d5ffd5b505060015461407a92506001600160a01b038c81169250165f61435e565b8c7fac9fe8ad7f55eac03284399116ecafc104f10459773f4cdf47063c46e5be335a8d866040516140ac9291906159c2565b60405180910390a260025f9054906101000a90046001600160a01b03166001600160a01b03166341489f158e8e8c6040518463ffffffff1660e01b81526004016140f89392919061592d565b5f604051808303815f87803b15801561410f575f5ffd5b505af1158015614121573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f828152600d602052604081205460ff1690816006811115614158576141586146a1565b0361417d57826001826040516337e1404160e01b815260040161098893929190614e28565b6005816006811115614191576141916146a1565b036141b25760405163462c7bed60e01b815260048101849052602401610988565b60068160068111156141c6576141c66146a1565b036141e757604051633de16e3560e11b815260048101849052602401610988565b5f838152600d6020526040902080546006919060ff191660018302179055505f838152600f60205260409020805483919060ff1916600183600d811115614230576142306146a1565b0217905550825f5160206159e75f395f51905f52826006604051614255929190614e70565b60405180910390a2827fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb8284604051612c73929190615778565b6040516001600160a01b0384811660248301528381166044830152606482018390526142c89186918216906323b872dd90608401613b09565b50505050565b6142d66143ee565b6112fe57604051631afcd79f60e31b815260040160405180910390fd5b612c886142ce565b5f5f60205f8451602086015f885af18061431a576040513d5f823e3d81fd5b50505f513d9150811561433157806001141561433e565b6001600160a01b0384163b155b156142c85783604051635274afe760e01b81526004016109889190614628565b5f836001600160a01b031663095ea7b38484604051602401614381929190615914565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505090506143ba8482614407565b6142c8576143e484856001600160a01b031663095ea7b3865f604051602401613b09929190615914565b6142c884826142fb565b5f6143f761394b565b54600160401b900460ff16919050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f519050828015614446575081156144385780600114614446565b5f866001600160a01b03163b115b9695505050505050565b604080516101e081019091525f808252602082019081526020015f815260200161447861459c565b81525f602082018190526040820181905260608083018290526080830181905260a0830182905260c0830182905260e08301829052610100830182905261012083015261014082018190526101609091015290565b60018301918390821561455e579160200282015f5b8382111561452c57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026144e2565b801561455c5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261452c565b505b5061456a9291506145ba565b5090565b826002810192821561455e579160200282015b8281111561455e578251825591602001919060010190614581565b60405180604001604052806002906020820280368337509192915050565b5b8082111561456a575f81556001016145bb565b6001600160a01b0381168114610acc575f5ffd5b5f602082840312156145f2575f5ffd5b81356145fd816145ce565b9392505050565b5f60208284031215614614575f5ffd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b80356004811061464a575f5ffd5b919050565b5f5f60408385031215614660575f5ffd5b6146698361463c565b946020939093013593505050565b5f60608284031215610ef1575f5ffd5b5f60608284031215614697575f5ffd5b6145fd8383614677565b634e487b7160e01b5f52602160045260245ffd5b600e81106146c5576146c56146a1565b9052565b6020810161396f82846146b5565b600481106146c5576146c56146a1565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8e8152614725602082018f6146d7565b8c60408201528b606082015261473e608082018c61461b565b60ff8a1660a08201526101c060c08201525f61475e6101c083018b6146e7565b61476b60e084018b61461b565b61477961010084018a61461b565b876101208401528661014084015282810361016084015261479a81876146e7565b9150506147ab61018083018561461b565b8215156101a08301529f9e505050505050505050505050505050565b805f5b60028110156142c85781518452602093840193909101906001016147ca565b805182525f602082015161480060208501826146d7565b5060408201516040840152606082015161481d60608501826147c7565b50608082015160a084015260a082015161483a60c085018261461b565b5060c082015160ff811660e08501525060e08201516102006101008501526148666102008501826146e7565b905061010083015161487c61012086018261461b565b5061012083015161489161014086018261461b565b506101408301516101608501526101608301516101808501526101808301518482036101a08601526148c382826146e7565b9150506101a08301516148da6101c086018261461b565b506101c08301518015156101e0860152613a6b565b602081525f6145fd60208301846147e9565b803561464a816145ce565b5f5f5f5f5f5f5f610120888a031215614923575f5ffd5b873561492e816145ce565b9650602088013561493e816145ce565b9550604088013561494e816145ce565b9450606088013561495e816145ce565b9350608088013561496e816145ce565b925060a088013591506149848960c08a01614677565b905092959891949750929550565b803560ff8116811461464a575f5ffd5b5f602082840312156149b2575f5ffd5b6145fd82614992565b602081525f6145fd60208301846146e7565b5f5f604083850312156149de575f5ffd5b8235915060208301356149f0816145ce565b809150509250929050565b5f5f60408385031215614a0c575f5ffd5b50508035926020909101359150565b5f5f83601f840112614a2b575f5ffd5b5081356001600160401b03811115614a41575f5ffd5b602083019150836020828501011115613cc1575f5ffd5b5f5f5f60408486031215614a6a575f5ffd5b614a7384614992565b925060208401356001600160401b03811115614a8d575f5ffd5b614a9986828701614a1b565b9497909650939450505050565b8151815260208083015190820152604080830151908201526060810161396f565b8215158152604081016145fd60208301846146b5565b5f5f5f5f5f60608688031215614af1575f5ffd5b8535945060208601356001600160401b03811115614b0d575f5ffd5b614b1988828901614a1b565b90955093505060408601356001600160401b03811115614b37575f5ffd5b614b4388828901614a1b565b969995985093965092949392505050565b5f60208284031215614b64575f5ffd5b81356001600160401b03811115614b79575f5ffd5b820161010081850312156145fd575f5ffd5b5f6101e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e0830151614be560e084018261461b565b50610100830151614bfd61010084018261ffff169052565b50610120830151614c1561012084018261ffff169052565b50610140830151614c2d61014084018261ffff169052565b50610160830151614c4561016084018261ffff169052565b50610180830151614c5d61018084018261ffff169052565b506101a0830151614c776101a084018263ffffffff169052565b506101c0830151614c916101c084018263ffffffff169052565b5092915050565b5f5f5f5f5f5f5f6080888a031215614cae575f5ffd5b8735965060208801356001600160401b03811115614cca575f5ffd5b614cd68a828b01614a1b565b90975095505060408801356001600160401b03811115614cf4575f5ffd5b614d008a828b01614a1b565b90955093505060608801356001600160401b03811115614d1e575f5ffd5b614d2a8a828b01614a1b565b989b979a50959850939692959293505050565b5f5f60408385031215614d4e575f5ffd5b82359150614d5e60208401614992565b90509250929050565b600781106146c5576146c56146a1565b6020810161396f8284614d67565b5f6101e0828403128015614d97575f5ffd5b509092915050565b5f5f60608385031215614db0575f5ffd5b614db98361463c565b915083606084011115614dca575f5ffd5b50926020919091019150565b828152604060208201525f614dee60408301846147e9565b949350505050565b600181811c90821680614e0a57607f821691505b602082108103610ef157634e487b7160e01b5f52602260045260245ffd5b83815260608101614e3c6020830185614d67565b614dee6040830184614d67565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561396f5761396f614e49565b60408101614e7e8285614d67565b6145fd6020830184614d67565b634e487b7160e01b5f52604160045260245ffd5b601f821115613b3b57805f5260205f20601f840160051c81016020851015614ec45750805b601f840160051c820191505b81811015614ee3575f8155600101614ed0565b5050505050565b5f19600383901b1c191660019190911b1790565b6001600160401b03831115614f1557614f15614e8b565b614f2983614f238354614df6565b83614e9f565b5f601f841160018114614f55575f8515614f435750838201355b614f4d8682614eea565b845550614ee3565b5f83815260208120601f198716915b82811015614f845786850135825560209485019460019092019101614f64565b5086821015614fa0575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b60ff84168152604060208201525f614ff6604083018486614fb2565b95945050505050565b5f8151808452602084019350602083015f5b828110156150385781516001600160a01b0316865260209586019590910190600101615011565b5093949350505050565b848152836020820152608060408201525f6150606080830185614fff565b905060018060a01b038316606083015295945050505050565b634e487b7160e01b5f52603260045260245ffd5b818382375f9101908152919050565b848152836020820152606060408201525f614446606083018486614fb2565b8015158114610acc575f5ffd5b5f602082840312156150d8575f5ffd5b81516145fd816150bb565b602081525f614dee602083018486614fb2565b6020808252601c908201527f42465620706172616d20736574206e6f74207265676973746572656400000000604082015260600190565b5f6020828403121561513d575f5ffd5b6145fd8261463c565b6020810161396f82846146d7565b808202811582820484141761396f5761396f614e49565b5f8261518557634e487b7160e01b5f52601260045260245ffd5b500490565b5f6020828403121561519a575f5ffd5b5051919050565b8181038181111561396f5761396f614e49565b858152606060208201525f6151cd606083018688614fb2565b82810360408401526151e0818587614fb2565b98975050505050505050565b604081525f6151ff604083018688614fb2565b8281036020840152615212818587614fb2565b979650505050505050565b61ffff81168114610acc575f5ffd5b803561464a8161521d565b5f60208284031215615247575f5ffd5b81356145fd8161521d565b63ffffffff81168114610acc575f5ffd5b803561464a81615252565b5f6020828403121561527e575f5ffd5b81356145fd81615252565b5f813561396f816145ce565b5f813561396f8161521d565b5f813561396f81615252565b813581556020820135600182015560408201356002820155606082013560038201556080820135600482015560a0820135600582015560c082013560068201556007810161531d61530060e08501615289565b82546001600160a01b0319166001600160a01b0391909116178255565b61534d61532d6101008501615295565b82805461ffff60a01b191660a09290921b61ffff60a01b16919091179055565b61537d61535d6101208501615295565b82805461ffff60b01b191660b09290921b61ffff60b01b16919091179055565b6153ad61538d6101408501615295565b82805461ffff60c01b191660c09290921b61ffff60c01b16919091179055565b6153dd6153bd6101608501615295565b82805461ffff60d01b191660d09290921b61ffff60d01b16919091179055565b61540d6153ed6101808501615295565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b50600881016154396154226101a085016152a1565b825463ffffffff191663ffffffff91909116178255565b613b3b6154496101c085016152a1565b825467ffffffff00000000191660209190911b67ffffffff0000000016178255565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c080830135908201526101e081016154bc60e08401614901565b6154c960e084018261461b565b506154d7610100840161522c565b61ffff166101008301526154ee610120840161522c565b61ffff16610120830152615505610140840161522c565b61ffff1661014083015261551c610160840161522c565b61ffff16610160830152615533610180840161522c565b61ffff1661018083015261554a6101a08401615263565b63ffffffff166101a08301526155636101c08401615263565b63ffffffff81166101c0840152614c91565b6040810181835f5b60028110156155ac57813561559181615252565b63ffffffff168352602092830192919091019060010161557d565b50505092915050565b5f600182016155c6576155c6614e49565b5060010190565b5f5f8335601e198436030181126155e2575f5ffd5b8301803591506001600160401b038211156155fb575f5ffd5b602001915036819003821315613cc1575f5ffd5b5f6020828403121561561f575f5ffd5b81356145fd816150bb565b87815286602082015260a060408201525f61564860a08301886146e7565b828103606084015261565b818789614fb2565b90508281036080840152615670818587614fb2565b9a9950505050505050505050565b81516001600160401b0381111561569757615697614e8b565b6156ab816156a58454614df6565b84614e9f565b6020601f8211600181146156d8575f83156156c65750848201515b6156d08482614eea565b855550614ee3565b5f84815260208120601f198516915b8281101561570757878501518255602094850194600190920191016156e7565b508482101561572457868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b838152602081018390526080810160408201835f5b600281101561576d57815163ffffffff16835260209283019290910190600101615748565b505050949350505050565b604081016157868285614d67565b6145fd60208301846146b5565b604051601f8201601f191681016001600160401b03811182821017156157bb576157bb614e8b565b604052919050565b5f6001600160401b038211156157db576157db614e8b565b5060051b60200190565b5f82601f8301126157f4575f5ffd5b8151615807615802826157c3565b615793565b8082825260208201915060208360051b860101925085831115615828575f5ffd5b602085015b8381101561584557805183526020928301920161582d565b5095945050505050565b5f5f60408385031215615860575f5ffd5b82516001600160401b03811115615875575f5ffd5b8301601f81018513615885575f5ffd5b8051615893615802826157c3565b8082825260208201915060208360051b8501019250878311156158b4575f5ffd5b6020840193505b828410156158df5783516158ce816145ce565b8252602093840193909101906158bb565b8095505050505060208301516001600160401b038111156158fe575f5ffd5b61590a858286016157e5565b9150509250929050565b6001600160a01b03929092168252602082015260400190565b838152606060208201525f6159456060830185614fff565b905060018060a01b0383166040830152949350505050565b5f8151808452602084019350602083015f5b8281101561503857815186526020958601959091019060010161596f565b6001600160a01b03841681526060602082018190525f906159b090830185614fff565b8281036040840152614446818561595d565b604081525f6159d46040830185614fff565b8281036020840152614ff6818561595d56fe1b418a230a21d37a078bf8f16decbde8ccceacd77159371f62f0d4ea00d19967a164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b50600436106102ce575f3560e01c80639117173c11610182578063cb649617116100e0578063f0691cba1161008f578063f0691cba14610886578063f2fde38b14610899578063f3ceba3a146108ac578063f81b8ef6146108cd578063fad8e111146108e0578063fbdb3237146108f3578063fd2f3d011461091b575f5ffd5b8063cb649617146107ef578063cbd16872146107f8578063cf0f34c41461080b578063cfbdc98d1461081e578063d8afed3e1461084d578063e59e469514610860578063ea71aa5714610873575f5ffd5b8063a87f4ab91161013c578063a87f4ab914610607578063ac3d2f4214610759578063b27392d514610781578063bb2d1b8214610794578063bff232c1146107a7578063c1ab0f1f146107ba578063c4ccafa2146107cd575f5ffd5b80639117173c146105625780639231238614610575578063929a8faf1461058857806399c6679d146105a95780639c8570c8146105d15780639e57b934146105f4575f5ffd5b80635d1684181161022f57806381476ec2116101e957806381476ec2146104e7578063830d7181146104fa578063858142431461050d5780638da5cb5b146105205780638dcdd86b146105285780638e5ce3ad1461053a57806390173a411461054d575f5ffd5b80635d1684181461047d578063647846a51461049d5780636db5c8fd146104b0578063715018a6146104b95780637c8c3b4d146104c15780637cfa9d74146104d4575f5ffd5b806336c5d38a1161028b57806336c5d38a1461039b5780634017daf0146103ca578063406ed35c146103f75780634147a360146104175780634d600e5d146104445780634e92ec63146104575780634fc772641461046a575f5ffd5b806302a3a9c9146102d25780630ef81b2f146102e757806310bc62811461032557806311bd61d91461034d57806315cce224146103755780631ba7294514610388575b5f5ffd5b6102e56102e03660046145e2565b61092e565b005b61030f6102f5366004614604565b5f908152600960205260409020546001600160a01b031690565b60405161031c9190614628565b60405180910390f35b61030f610333366004614604565b60096020525f90815260409020546001600160a01b031681565b61036061035b36600461464f565b6109da565b60405163ffffffff909116815260200161031c565b6102e56103833660046145e2565b610a16565b6102e5610396366004614687565b610abb565b6103bd6103a9366004614604565b5f908152600f602052604090205460ff1690565b60405161031c91906146c9565b6103dd6103d8366004614604565b610acf565b60405161031c9e9d9c9b9a99989796959493929190614715565b61040a610405366004614604565b610c7a565b60405161031c91906148ef565b610436610425366004614604565b600c6020525f908152604090205481565b60405190815260200161031c565b6102e561045236600461490c565b610ef7565b6102e5610465366004614604565b611134565b6102e56104783660046145e2565b6111c3565b61049061048b3660046149a2565b611256565b60405161031c91906149bb565b60045461030f906001600160a01b031681565b61043660055481565b6102e56112ed565b6102e56104cf3660046149cd565b611300565b6102e56104e2366004614604565b611389565b6102e56104f53660046149fb565b611488565b6102e5610508366004614a58565b61157c565b60015461030f906001600160a01b031681565b61030f611688565b5f5461030f906001600160a01b031681565b60035461030f906001600160a01b031681565b6105556116b6565b60405161031c9190614aa6565b6102e5610570366004614604565b6116fc565b610555610583366004614604565b61186a565b61059b610596366004614604565b6118c3565b60405161031c929190614ac7565b61030f6105b7366004614604565b5f908152601060205260409020546001600160a01b031690565b6105e46105df366004614add565b6118ea565b604051901515815260200161031c565b610436610602366004614b54565b611b82565b61074c604080516101e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915250604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a0840152640100000000909104166101c082015290565b60405161031c9190614b8b565b61030f610767366004614604565b5f908152600a60205260409020546001600160a01b031690565b6105e461078f366004614c98565b612157565b6102e56107a2366004614d3d565b612395565b6102e56107b53660046145e2565b612451565b6102e56107c83660046149fb565b6124f8565b6105e46107db3660046145e2565b60076020525f908152604090205460ff1681565b61043660065481565b6102e56108063660046149cd565b6125b5565b6102e5610819366004614604565b61266f565b61084061082c366004614604565b5f908152600d602052604090205460ff1690565b60405161031c9190614d77565b6102e561085b366004614d85565b6126ac565b6102e561086e3660046145e2565b612939565b6102e5610881366004614d9f565b6129d3565b60025461030f906001600160a01b031681565b6102e56108a73660046145e2565b612c80565b6108bf6108ba366004614b54565b612cba565b60405161031c929190614dd6565b6103bd6108db366004614604565b61359b565b6102e56108ee3660046145e2565b613735565b61030f610901366004614604565b600a6020525f90815260409020546001600160a01b031681565b6102e56109293660046145e2565b6137cd565b61093661385c565b6001600160a01b0381166109915760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964204533526566756e644d616e6167657220616464726573730060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040517f9557d04c1c0b16f93f13b69aed23b3b6ab935bff3c53ac81d17896d3583542ed905f90a250565b6012602052815f5260405f2081600281106109f3575f80fd5b60089182820401919006600402915091509054906101000a900463ffffffff1681565b610a1e61385c565b6001600160a01b03811615801590610a4457506004546001600160a01b03828116911614155b8190610a645760405163eddf07f560e01b81526004016109889190614628565b50600480546001600160a01b0319166001600160a01b0383161790556040517f722ff84c1234b2482061def5c82c6b5080c117b3cbb69d686844a051e4b8e7f390610ab0908390614628565b60405180910390a150565b610ac361385c565b610acc8161388e565b50565b60086020525f9081526040902080546001820154600283015460058401546006850154600786018054959660ff95861696949593946001600160a01b03841694600160a01b90940490931692909190610b2790614df6565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5390614df6565b8015610b9e5780601f10610b7557610100808354040283529160200191610b9e565b820191905f5260205f20905b815481529060010190602001808311610b8157829003601f168201915b50505060088401546009850154600a860154600b870154600c8801805497986001600160a01b03958616989490951696509194509291610bdd90614df6565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0990614df6565b8015610c545780601f10610c2b57610100808354040283529160200191610c54565b820191905f5260205f20905b815481529060010190602001808311610c3757829003601f168201915b505050600d90930154919250506001600160a01b0381169060ff600160a01b909104168e565b610c82614450565b5f8281526008602090815260409182902082516101e08101909352805483526001810154909183019060ff166003811115610cbf57610cbf6146a1565b6003811115610cd057610cd06146a1565b8152600282810154602083015260408051808201808352919093019291600385019182845b815481526020019060010190808311610cf55750505091835250506005820154602082015260068201546001600160a01b0381166040830152600160a01b900460ff166060820152600782018054608090920191610d5290614df6565b80601f0160208091040260200160405190810160405280929190818152602001828054610d7e90614df6565b8015610dc95780601f10610da057610100808354040283529160200191610dc9565b820191905f5260205f20905b815481529060010190602001808311610dac57829003601f168201915b505050918352505060088201546001600160a01b0390811660208301526009830154166040820152600a8201546060820152600b8201546080820152600c8201805460a090920191610e1a90614df6565b80601f0160208091040260200160405190810160405280929190818152602001828054610e4690614df6565b8015610e915780601f10610e6857610100808354040283529160200191610e91565b820191905f5260205f20905b815481529060010190602001808311610e7457829003601f168201915b5050509183525050600d91909101546001600160a01b038082166020840152600160a01b90910460ff16151560409092019190915260a0820151919250839116610ef15760405163cd6f4a4f60e01b815260040161098891815260200190565b50919050565b5f610f0061394b565b805490915060ff600160401b82041615906001600160401b03165f81158015610f265750825b90505f826001600160401b03166001148015610f415750303b155b905081158015610f4f575080155b15610f6d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f9757845460ff60401b1916600160401b1785555b610fa033613975565b610fa98761266f565b610fb28b613735565b610fbb8a612939565b610fc48961092e565b610fcd88610a16565b610fd68661388e565b604080516101e08101825261c3508082526161a86020808401829052611388948401859052601460608501819052620249f0608086018190526207a12060a087018190526107d060c088018190525f60e089018190526103e86101008a015261012089018190526109c46101408a018190526101608a018b90526101808a01526101a089018190526101c090980197909752601895909555601993909355601a95909555601b94909455601c55601d55601e55601f80546001600160f01b031916690138827101388000007d60a31b179055805467ffffffffffffffff191690556110bf611688565b6001600160a01b03168c6001600160a01b0316146110e0576110e08c612c80565b831561112657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050565b61113c61385c565b5f8181526009602052604090205481906001600160a01b0316611175576040516381c4951960e01b815260040161098891815260200190565b505f818152600960205260409081902080546001600160a01b0319169055517f104eb329a192aef26eddea07c2af5ad2587792e62b37ed4045b6ba59bc5540fc90610ab09083815260200190565b6111cb61385c565b6001600160a01b0381165f90815260076020526040902054819060ff16611206576040516321ac7c5f60e01b81526004016109889190614628565b506001600160a01b0381165f9081526007602052604090819020805460ff19169055517f56070b80bd617fcd2f7a284861edb488830a38f9dedcd77b2cb2f4eac17743e790610ab0908390614628565b600b6020525f90815260409020805461126e90614df6565b80601f016020809104026020016040519081016040528092919081815260200182805461129a90614df6565b80156112e55780601f106112bc576101008083540402835291602001916112e5565b820191905f5260205f20905b8154815290600101906020018083116112c857829003601f168201915b505050505081565b6112f561385c565b6112fe5f613986565b565b61130861385c565b6001600160a01b0381161580159061133957505f828152600a60205260409020546001600160a01b03828116911614155b829061135b576040516381c4951960e01b815260040161098891815260200190565b505f918252600a602052604090912080546001600160a01b0319166001600160a01b03909216919091179055565b5f546001600160a01b031633146113b35760405163b56831db60e01b815260040160405180910390fd5b5f818152600d602052604090205460ff1660018160068111156113d8576113d86146a1565b146113fd57816001826040516337e1404160e01b815260040161098893929190614e28565b5f828152600d60205260409020805460ff191660021790556015546114229042614e5d565b5f838152600e602052604080822092909255905183917fc44405af9078047712501f519e1fb900c2896c62b488336f84529c72ae16e6f191a2815f5160206159e75f395f51905f526001600260405161147c929190614e70565b60405180910390a25050565b5f546001600160a01b031633146114b25760405163b56831db60e01b815260040160405180910390fd5b5f828152600860209081526040808320600d9092529091205460ff1660028160068111156114e2576114e26146a1565b1461150757836002826040516337e1404160e01b815260040161098893929190614e28565b5f848152600d6020526040808220805460ff19166003179055600a84018590555185917f11df18edb9bc9cd90a79068e0e208b630202148643d797d6150e7bacb733e63c91a2835f5160206159e75f395f51905f526002600360405161156e929190614e70565b60405180910390a250505050565b61158461385c565b806115c05760405162461bcd60e51b815260206004820152600c60248201526b456d70747920706172616d7360a01b6044820152606401610988565b60ff83165f908152600b6020526040902080546115dc90614df6565b15905061162b5760405162461bcd60e51b815260206004820152601b60248201527f506172616d53657420616c7265616479207265676973746572656400000000006044820152606401610988565b60ff83165f908152600b60205260409020611647828483614efe565b507f6e4a4ea7f38fc775e616080b155744337e6216848e886a69c918b4ab84da219583838360405161167b93929190614fda565b60405180910390a1505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6116d760405180606001604052805f81526020015f81526020015f81525090565b5060408051606081018252601554815260165460208201526017549181019190915290565b5f818152600d602052604090205460ff166006816006811115611721576117216146a1565b14829061174457604051637cb2d48360e11b815260040161098891815260200190565b505f828152600c60205260409020548281611775576040516345ba89d560e11b815260040161098891815260200190565b505f838152600c6020526040812081905561178f846139f6565b5f858152601160205260409020546002549192506001600160a01b03908116916117bc9183911685613ae3565b60025460405163da19b69760e01b81526001600160a01b039091169063da19b697906117f2908890879087908790600401615042565b5f604051808303815f87803b158015611809575f5ffd5b505af115801561181b573d5f5f3e3d5ffd5b50505050847f5297818f48a66292b8b3e2caab83eec531b669bb20807fd38cf006adb2a0731784845160405161185b929190918252602082015260400190565b60405180910390a25050505050565b61188b60405180606001604052805f81526020015f81526020015f81525090565b505f908152600e6020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b5f818152600d6020526040812054819060ff166118e08482613b40565b9250925050915091565b5f5f6118f587610c7a565b5f888152600d602052604090205490915060ff16600381600681111561191d5761191d6146a1565b1488600383909192611945576040516337e1404160e01b815260040161098893929190614e28565b5050505f888152600e60209081526040918290208251606081018452815481526001820154928101839052600290910154928101929092528990428110156119a9576040516308f3034360e31b815260048101929092526024820152604401610988565b50506060830151602001518990428111156119e05760405163017e35e560e71b815260048101929092526024820152604401610988565b5050610160830151899015611a0b57604051637eb9cea960e11b815260040161098891815260200190565b505f8888604051611a1d92919061508d565b60408051918290039091205f8c815260086020908152838220600b01839055600d905291909120805460ff19166004179055601754909150611a5f9042614e5d565b5f8b8152600e6020526040908190206002019190915560a08501519051632f0e1bbf60e01b81526001600160a01b0390911690632f0e1bbf90611aac908d9085908c908c9060040161509c565b6020604051808303815f875af1158015611ac8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611aec91906150c8565b9450888886611b1057604051632f9f8ab960e01b81526004016109889291906150e3565b5050897f7cc27e4a5626cbc4f8ba1a927b0448de55e6a114bc87660331270c5109ade0718a8a604051611b449291906150e3565b60405180910390a2895f5160206159e75f395f51905f5260036004604051611b6d929190614e70565b60405180910390a25050505095945050505050565b5f80600b81611b9760a08601608087016149a2565b60ff1660ff1681526020019081526020015f208054611bb590614df6565b905011611bd45760405162461bcd60e51b8152600401610988906150f6565b5f601281611be5602086018661512d565b6003811115611bf657611bf66146a1565b6003811115611c0757611c076146a1565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411611c2e579050505050505090505f81600160028110611c8557611c85615079565b602002015163ffffffff1611835f016020810190611ca3919061512d565b90611cc25760405163286c068d60e11b81526004016109889190615146565b506020808201518251604080516101e081018252601854815260195481860152601a5491810191909152601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e0830152600160a01b810461ffff908116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152925463ffffffff8181166101a0860181905264010000000090920481166101c086015292831693919092169115611ded576101a081015163ffffffff16846001602002015163ffffffff161015865f016020810190611dcc919061512d565b90611deb5760405163010b971d60e31b81526004016109889190615146565b505b6101c081015163ffffffff1615611e3c576101c081015184519063ffffffff9081169082161015611e3a57604051630a4b6b6360e11b815263ffffffff9091166004820152602401610988565b505b60408601356020870135811015611e695760405163174b5a0760e21b815260040161098891815260200190565b506101808101516017545f9161271091611e879161ffff1690615154565b611e91919061516b565b61271061ffff1683610160015161ffff16601560010154611eb29190615154565b611ebc919061516b565b61271061ffff1684610140015161ffff1660155f0154611edc9190615154565b611ee6919061516b565b5f5460408051634f87c3a560e11b8152815160208e81013594938f0135936001600160a01b031692639f0f874a92600480830193928290030181865afa158015611f32573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f56919061518a565b611f609190614e5d565b611f6a91906151a1565b611f749190614e5d565b611f7e9190614e5d565b611f889190614e5d565b90505f611f966001866151a1565b611fa1906002615154565b611fac906002615154565b611fb7906006614e5d565b90505f85845f0151611fc99190615154565b905081868560200151611fdc9190615154565b611fe69190615154565b611ff09082614e5d565b905060018611156120385760026120086001886151a1565b6120129088615154565b85604001516120219190615154565b61202b919061516b565b6120359082614e5d565b90505b81868560c001516120499190615154565b6120539190615154565b61205d9082614e5d565b9050828685606001516120709190615154565b61207a9190615154565b6120849082614e5d565b90508484608001516120969190615154565b6120a09082614e5d565b905060018511156120e85760026120b86001876151a1565b6120c29087615154565b85604001516120d19190615154565b6120db919061516b565b6120e59082614e5d565b90505b60a08401516120f79082614e5d565b610100850151909150612710906121129061ffff1682614e5d565b61211c9083615154565b612126919061516b565b9750878061214a57604051638c4fcd9360e01b815260040161098891815260200190565b5050505050505050919050565b5f5f61216289610c7a565b5f8a8152600d602052604090205490915060ff16600481600681111561218a5761218a6146a1565b148a6004839091926121b2576040516337e1404160e01b815260040161098893929190614e28565b5050505f8a8152600e602090815260409182902082516060810184528154815260018201549281019290925260020154918101829052908b9042811015612215576040516308f3034360e31b815260048101929092526024820152604401610988565b50505f8b8152600860205260409020600c016122328a8c83614efe565b505f8b8152600d60205260409020805460ff191660051790556101c083015115612312578261010001516001600160a01b0316635bf48e3a8b8b60405161227a92919061508d565b6040519081900381206001600160e01b031960e084901b1682526122a8918c908c908c908c906004016151b4565b602060405180830381865afa1580156122c3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122e791906150c8565b935089898561230b57604051632f9f8ab960e01b81526004016109889291906150e3565b5050612317565b600193505b6123208b613cc8565b8a7f3a140076c461ebc41d74833ae0ee8bbc8079a135a63392098cd381e84350b69b8b8b8b8b60405161235694939291906151ec565b60405180910390a28a5f5160206159e75f395f51905f526004600560405161237f929190614e70565b60405180910390a2505050979650505050505050565b5f546001600160a01b03163314806123b757506003546001600160a01b031633145b6123d457604051639e75a8b560e01b815260040160405180910390fd5b5f8160ff161180156123ea5750600d60ff821611155b61242f5760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b2103330b4b63ab932903932b0b9b7b760511b6044820152606401610988565b61244d828260ff16600d811115612448576124486146a1565b614134565b5050565b61245961385c565b6001600160a01b0381166124af5760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420536c617368696e674d616e616765722061646472657373006044820152606401610988565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6003546001600160a01b03163314612523576040516357d6948d60e11b815260040160405180910390fd5b60025460405163c1ab0f1f60e01b815260048101849052602481018390526001600160a01b039091169063c1ab0f1f906044015f604051808303815f87803b15801561256d575f5ffd5b505af115801561257f573d5f5f3e3d5ffd5b50505050817f4f41a3b0a032ebcae925f2ace77d507435840ca4b2dbaffdd7723fa8d72ee5428260405161147c91815260200190565b6125bd61385c565b6001600160a01b038116158015906125ee57505f828152600960205260409020546001600160a01b03828116911614155b8290612610576040516381c4951960e01b815260040161098891815260200190565b505f8281526009602090815260409182902080546001600160a01b0319166001600160a01b03851617905590518381527ff4041a3f914dac3bc9bf5f003ba41f28dbb84abe42f4e07c76266f5c8ceecb69910160405180910390a15050565b61267761385c565b60058190556040518181527fba0716ba1ee2ea8ecc4c64119b4537cdb42a99d82acf92af5b87607b8b52355290602001610ab0565b6126b461385c565b6127106126c961012083016101008401615237565b61ffff1611156126e161012083016101008401615237565b90612706576040516301027fc160e21b815261ffff9091166004820152602401610988565b5061271061271c61014083016101208401615237565b61ffff16111561273461014083016101208401615237565b90612759576040516301027fc160e21b815261ffff9091166004820152602401610988565b5061271061276f61016083016101408401615237565b61ffff16111561278761016083016101408401615237565b906127ac57604051633239953960e01b815261ffff9091166004820152602401610988565b506127106127c261018083016101608401615237565b61ffff1611156127da61018083016101608401615237565b906127ff57604051633239953960e01b815261ffff9091166004820152602401610988565b506127106128156101a083016101808401615237565b61ffff16111561282d6101a083016101808401615237565b9061285257604051633239953960e01b815261ffff9091166004820152602401610988565b5061286561014082016101208301615237565b61ffff16158061288e57505f612882610100830160e084016145e2565b6001600160a01b031614155b6128ab5760405163015f92ff60e51b815260040160405180910390fd5b6128bd6101e082016101c0830161526e565b63ffffffff166128d56101c083016101a0840161526e565b63ffffffff1610156128fa576040516392f55c6560e01b815260040160405180910390fd5b80601861290782826152ad565b9050507fbf3951313e980027eb48ce363fdb707286195ec6a0f802ac153927cf929c3fc681604051610ab0919061546b565b61294161385c565b6001600160a01b0381161580159061296757506001546001600160a01b03828116911614155b8190612987576040516320252f0b60e01b81526004016109889190614628565b50600180546001600160a01b0319166001600160a01b0383161790556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790610ab0908390614628565b6129db61385c565b6129e8602082018261526e565b63ffffffff166129fe604083016020840161526e565b63ffffffff1610158015612a2357505f612a1b602083018361526e565b63ffffffff16115b612a4057604051634564ab9b60e01b815260040160405180910390fd5b604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a08401819052640100000000909204166101c083015215612b79576101a081015163ffffffff16612b29604084016020850161526e565b63ffffffff161015612b41604084016020850161526e565b826101a001519091612b7657604051633ccc4c2160e21b815263ffffffff928316600482015291166024820152604401610988565b50505b6101c081015163ffffffff1615612bf0576101c081015163ffffffff16612ba3602084018461526e565b63ffffffff161015612bb8602084018461526e565b826101c001519091612bed5760405163156c4e5b60e11b815263ffffffff928316600482015291166024820152604401610988565b50505b8160125f856003811115612c0657612c066146a1565b6003811115612c1757612c176146a1565b815260208101919091526040015f20612c319160026144cd565b50826003811115612c4457612c446146a1565b7f8b56fae526eee054f0849759a99fc7d4ff3823824ebf097a56f7d78adb6b34fa83604051612c739190615575565b60405180910390a2505050565b612c8861385c565b6001600160a01b038116612cb1575f604051631e4fbdf760e01b81526004016109889190614628565b610acc81613986565b5f612cc3614450565b5f601281612cd4602087018761512d565b6003811115612ce557612ce56146a1565b6003811115612cf657612cf66146a1565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612d1d579050505050505090505f81600160028110612d7457612d74615079565b602002015163ffffffff1611845f016020810190612d92919061512d565b90612db15760405163286c068d60e11b81526004016109889190615146565b50602084013542811015612ddb57604051630b99e87960e01b815260040161098891815260200190565b5060408401356020850135811015612e095760405163174b5a0760e21b815260040161098891815260200190565b506017546016545f9190612e214260408901356151a1565b612e2b9190614e5d565b612e359190614e5d565b905060055481108190612e5e576040516313b783af60e21b815260040161098891815260200190565b5060075f612e7260808801606089016145e2565b6001600160a01b0316815260208101919091526040015f205460ff16612e9e60808701606088016145e2565b90612ebd5760405163295a6a6f60e11b81526004016109889190614628565b505f612ec886611b82565b60068054965090915085905f612edd836155b5565b9091555050604080514460208201529081018690525f9060600160408051601f1981840301815291815281516020928301205f898152600c84528281208690556004546011855283822080546001600160a01b03199081166001600160a01b0393841617909155601f805460138852868520805461ffff191661ffff600160b01b909304929092169190911790555460148752858420805483169190931617909155600d8552838220805460ff191660011790556010909452829020805433941693909317909255601654919250612fb89190890135614e5d565b5f878152600e6020908152604090912060010191909155818652612fde9088018861512d565b85602001906003811115612ff457612ff46146a1565b90816003811115613007576130076146a1565b905250436040808701919091528051808201825290602089019060029083908390808284375f92019190915250505060608087019190915261304f90608089019089016145e2565b6001600160a01b031660a080870191909152613070908801608089016149a2565b60ff1660c080870191909152613088908801886155cd565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060e0808701919091526130d590610100890190890161560f565b15156101c08601525f610140860181905261016086018190526040805160208101909152908152610180860152336101a08601819052600454613125916001600160a01b0390911690308561428f565b5f600b8161313960a08b0160808c016149a2565b60ff1660ff1681526020019081526020015f20805461315790614df6565b80601f016020809104026020016040519081016040528092919081815260200182805461318390614df6565b80156131ce5780601f106131a5576101008083540402835291602001916131ce565b820191905f5260205f20905b8154815290600101906020018083116131b157829003601f168201915b505050505090505f8151116131f55760405162461bcd60e51b8152600401610988906150f6565b5f61320660808a0160608b016145e2565b6001600160a01b031663fefd9a8b89858561322460a08f018f6155cd565b8f8060c0019061323491906155cd565b6040518863ffffffff1660e01b8152600401613256979695949392919061562a565b6020604051808303815f875af1158015613272573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613296919061518a565b5f818152600960205260409020549091506001600160a01b031681816132d2576040516381c4951960e01b815260040161098891815260200190565b505f828152600a60205260409020546001600160a01b0316828161330c576040516381c4951960e01b815260040161098891815260200190565b50608089018390526001600160a01b038083166101008b015281166101208a01525f8a81526008602090815260409091208a518155908a0151600180830180548d94939260ff19919091169083600381111561336a5761336a6146a1565b02179055506040820151816002015560608201518160030190600261339092919061456e565b506080820151600582015560a082015160068201805460c085015160ff16600160a01b026001600160a81b03199091166001600160a01b039093169290921791909117905560e082015160078201906133e9908261567e565b506101008201516008820180546001600160a01b039283166001600160a01b031991821617909155610120840151600984018054919093169116179055610140820151600a820155610160820151600b820155610180820151600c820190613451908261567e565b506101a0820151600d90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091555f5460405163291a691b60e01b815291169063291a691b906134b7908d9089908d90600401615733565b6020604051808303815f875af11580156134d3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134f791906150c8565b61351457604051630d8dbe2560e01b815260040160405180910390fd5b61352460808c0160608d016145e2565b6001600160a01b03167f5090c9764b5cd13df7afc0013f733dfbe6eaf1b6ddc22a5e291fa387efd4c15e8b8b60405161355e929190614dd6565b60405180910390a2895f5160206159e75f395f51905f525f6001604051613586929190614e70565b60405180910390a25050505050505050915091565b5f818152600d602052604081205460ff16818160068111156135bf576135bf6146a1565b036135e457826001826040516337e1404160e01b815260040161098893929190614e28565b60058160068111156135f8576135f86146a1565b036136195760405163462c7bed60e01b815260048101849052602401610988565b600681600681111561362d5761362d6146a1565b0361364e57604051633de16e3560e11b815260048101849052602401610988565b5f6136598483613b40565b935090508061367e57604051639f65d93560e01b815260048101859052602401610988565b5f848152600d6020526040902080546006919060ff191660018302179055505f848152600f60205260409020805484919060ff1916600183600d8111156136c7576136c76146a1565b0217905550835f5160206159e75f395f51905f528360066040516136ec929190614e70565b60405180910390a2837fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb8385604051613726929190615778565b60405180910390a25050919050565b61373d61385c565b6001600160a01b0381161580159061376257505f546001600160a01b03828116911614155b8190613782576040516375ac4eb760e11b81526004016109889190614628565b505f80546001600160a01b0319166001600160a01b0383161790556040517f80052b810d39120cf6c976cca504a21703f585521dc7a41c6d241090e6c579b690610ab0908390614628565b6001600160a01b0381165f90815260076020526040902054819060ff16156138095760405163b29d459560e01b81526004016109889190614628565b506001600160a01b0381165f9081526007602052604090819020805460ff19166001179055517fb8d368517268f297fff00825d67d098763117d061360d31027be5b2e1a59d46790610ab0908390614628565b33613865611688565b6001600160a01b0316146112fe573360405163118cdaa760e01b81526004016109889190614628565b80356138ad5760405163055f269d60e01b815260040160405180910390fd5b5f8160200135116138d15760405163055f269d60e01b815260040160405180910390fd5b5f8160400135116138f55760405163055f269d60e01b815260040160405180910390fd5b80356015819055602080830135601681905560408085013560178190558151948552928401919091528201527f7e86ba16b805e2835af5c5b7aa5a942ced8bcc1fb95a05fbe42dae3862350a1690606001610ab0565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b61397d6142ce565b610acc816142f3565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f818152600f602052604090205460609060ff16600181600d811115613a1e57613a1e6146a1565b1480613a3b5750600281600d811115613a3957613a396146a1565b145b15613a73575f5b604051908082528060200260200182016040528015613a6b578160200160208202803683370190505b509392505050565b5f5460405162beb08960e51b8152600481018590526001600160a01b03909116906317d61120906024015f60405180830381865afa925050508015613ad957506040513d5f823e601f3d908101601f19168201604052613ad6919081019061584f565b60015b613a6b575f613a42565b613b3b83846001600160a01b031663a9059cbb8585604051602401613b09929190615914565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506142fb565b505050565b5f828152600e60209081526040808320815160608101835281548152600182015493810193909352600201548282015282549051632800d82960e01b81526004810186905283929183916001600160a01b0390911690632800d82990602401602060405180830381865afa158015613bba573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bde919061518a565b90506001856006811115613bf457613bf46146a1565b148015613c0057508042115b15613c1357600180935093505050613cc1565b6002856006811115613c2757613c276146a1565b148015613c345750815142115b15613c485760016003935093505050613cc1565b6003856006811115613c5c57613c5c6146a1565b148015613c6c5750816020015142115b15613c805760016006935093505050613cc1565b6004856006811115613c9457613c946146a1565b148015613ca45750816040015142115b15613cb8576001600a935093505050613cc1565b5f5f9350935050505b9250929050565b5f805460405162beb08960e51b8152600481018490526001600160a01b03909116906317d61120906024015f60405180830381865afa158015613d0d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613d34919081019061584f565b5080515f848152600c60209081526040808320805490849055601190925282205493945091926001600160a01b031690829003613dd3576002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613d9f9088908890869060040161592d565b5f604051808303815f87803b158015613db6575f5ffd5b505af1158015613dc8573d5f5f3e3d5ffd5b505050505050505050565b825f03613e74575f858152601060205260409020546001600160a01b03168015613e0b57613e0b6001600160a01b0383168285613ae3565b6002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613e3f9089908990879060040161592d565b5f604051808303815f87803b158015613e56575f5ffd5b505af1158015613e68573d5f5f3e3d5ffd5b50505050505050505050565b5f85815260136020908152604080832054601490925282205461ffff909116906001600160a01b03168115801590613eb457506001600160a01b03811615155b15613ef057612710613eca61ffff841687615154565b613ed4919061516b565b92508215613ef057613ef06001600160a01b0385168285613ae3565b5f613efb84876151a1565b90505f876001600160401b03811115613f1657613f16614e8b565b604051908082528060200260200182016040528015613f3f578160200160208202803683370190505b5090505f613f4d898461516b565b90505f805b8a811015613f8c5782848281518110613f6d57613f6d615079565b6020908102919091010152613f828383614e5d565b9150600101613f52565b505f613f9882866151a1565b90508015613fd5578084613fad60018e6151a1565b81518110613fbd57613fbd615079565b60200260200101818151613fd19190614e5d565b9052505b600154613fef906001600160a01b038b811691168761435e565b60015f9054906101000a90046001600160a01b03166001600160a01b031663dd8c818e8a8e876040518463ffffffff1660e01b81526004016140339392919061598d565b5f604051808303815f87803b15801561404a575f5ffd5b505af115801561405c573d5f5f3e3d5ffd5b505060015461407a92506001600160a01b038c81169250165f61435e565b8c7fac9fe8ad7f55eac03284399116ecafc104f10459773f4cdf47063c46e5be335a8d866040516140ac9291906159c2565b60405180910390a260025f9054906101000a90046001600160a01b03166001600160a01b03166341489f158e8e8c6040518463ffffffff1660e01b81526004016140f89392919061592d565b5f604051808303815f87803b15801561410f575f5ffd5b505af1158015614121573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f828152600d602052604081205460ff1690816006811115614158576141586146a1565b0361417d57826001826040516337e1404160e01b815260040161098893929190614e28565b6005816006811115614191576141916146a1565b036141b25760405163462c7bed60e01b815260048101849052602401610988565b60068160068111156141c6576141c66146a1565b036141e757604051633de16e3560e11b815260048101849052602401610988565b5f838152600d6020526040902080546006919060ff191660018302179055505f838152600f60205260409020805483919060ff1916600183600d811115614230576142306146a1565b0217905550825f5160206159e75f395f51905f52826006604051614255929190614e70565b60405180910390a2827fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb8284604051612c73929190615778565b6040516001600160a01b0384811660248301528381166044830152606482018390526142c89186918216906323b872dd90608401613b09565b50505050565b6142d66143ee565b6112fe57604051631afcd79f60e31b815260040160405180910390fd5b612c886142ce565b5f5f60205f8451602086015f885af18061431a576040513d5f823e3d81fd5b50505f513d9150811561433157806001141561433e565b6001600160a01b0384163b155b156142c85783604051635274afe760e01b81526004016109889190614628565b5f836001600160a01b031663095ea7b38484604051602401614381929190615914565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505090506143ba8482614407565b6142c8576143e484856001600160a01b031663095ea7b3865f604051602401613b09929190615914565b6142c884826142fb565b5f6143f761394b565b54600160401b900460ff16919050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f519050828015614446575081156144385780600114614446565b5f866001600160a01b03163b115b9695505050505050565b604080516101e081019091525f808252602082019081526020015f815260200161447861459c565b81525f602082018190526040820181905260608083018290526080830181905260a0830182905260c0830182905260e08301829052610100830182905261012083015261014082018190526101609091015290565b60018301918390821561455e579160200282015f5b8382111561452c57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026144e2565b801561455c5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261452c565b505b5061456a9291506145ba565b5090565b826002810192821561455e579160200282015b8281111561455e578251825591602001919060010190614581565b60405180604001604052806002906020820280368337509192915050565b5b8082111561456a575f81556001016145bb565b6001600160a01b0381168114610acc575f5ffd5b5f602082840312156145f2575f5ffd5b81356145fd816145ce565b9392505050565b5f60208284031215614614575f5ffd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b80356004811061464a575f5ffd5b919050565b5f5f60408385031215614660575f5ffd5b6146698361463c565b946020939093013593505050565b5f60608284031215610ef1575f5ffd5b5f60608284031215614697575f5ffd5b6145fd8383614677565b634e487b7160e01b5f52602160045260245ffd5b600e81106146c5576146c56146a1565b9052565b6020810161396f82846146b5565b600481106146c5576146c56146a1565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8e8152614725602082018f6146d7565b8c60408201528b606082015261473e608082018c61461b565b60ff8a1660a08201526101c060c08201525f61475e6101c083018b6146e7565b61476b60e084018b61461b565b61477961010084018a61461b565b876101208401528661014084015282810361016084015261479a81876146e7565b9150506147ab61018083018561461b565b8215156101a08301529f9e505050505050505050505050505050565b805f5b60028110156142c85781518452602093840193909101906001016147ca565b805182525f602082015161480060208501826146d7565b5060408201516040840152606082015161481d60608501826147c7565b50608082015160a084015260a082015161483a60c085018261461b565b5060c082015160ff811660e08501525060e08201516102006101008501526148666102008501826146e7565b905061010083015161487c61012086018261461b565b5061012083015161489161014086018261461b565b506101408301516101608501526101608301516101808501526101808301518482036101a08601526148c382826146e7565b9150506101a08301516148da6101c086018261461b565b506101c08301518015156101e0860152613a6b565b602081525f6145fd60208301846147e9565b803561464a816145ce565b5f5f5f5f5f5f5f610120888a031215614923575f5ffd5b873561492e816145ce565b9650602088013561493e816145ce565b9550604088013561494e816145ce565b9450606088013561495e816145ce565b9350608088013561496e816145ce565b925060a088013591506149848960c08a01614677565b905092959891949750929550565b803560ff8116811461464a575f5ffd5b5f602082840312156149b2575f5ffd5b6145fd82614992565b602081525f6145fd60208301846146e7565b5f5f604083850312156149de575f5ffd5b8235915060208301356149f0816145ce565b809150509250929050565b5f5f60408385031215614a0c575f5ffd5b50508035926020909101359150565b5f5f83601f840112614a2b575f5ffd5b5081356001600160401b03811115614a41575f5ffd5b602083019150836020828501011115613cc1575f5ffd5b5f5f5f60408486031215614a6a575f5ffd5b614a7384614992565b925060208401356001600160401b03811115614a8d575f5ffd5b614a9986828701614a1b565b9497909650939450505050565b8151815260208083015190820152604080830151908201526060810161396f565b8215158152604081016145fd60208301846146b5565b5f5f5f5f5f60608688031215614af1575f5ffd5b8535945060208601356001600160401b03811115614b0d575f5ffd5b614b1988828901614a1b565b90955093505060408601356001600160401b03811115614b37575f5ffd5b614b4388828901614a1b565b969995985093965092949392505050565b5f60208284031215614b64575f5ffd5b81356001600160401b03811115614b79575f5ffd5b820161010081850312156145fd575f5ffd5b5f6101e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e0830151614be560e084018261461b565b50610100830151614bfd61010084018261ffff169052565b50610120830151614c1561012084018261ffff169052565b50610140830151614c2d61014084018261ffff169052565b50610160830151614c4561016084018261ffff169052565b50610180830151614c5d61018084018261ffff169052565b506101a0830151614c776101a084018263ffffffff169052565b506101c0830151614c916101c084018263ffffffff169052565b5092915050565b5f5f5f5f5f5f5f6080888a031215614cae575f5ffd5b8735965060208801356001600160401b03811115614cca575f5ffd5b614cd68a828b01614a1b565b90975095505060408801356001600160401b03811115614cf4575f5ffd5b614d008a828b01614a1b565b90955093505060608801356001600160401b03811115614d1e575f5ffd5b614d2a8a828b01614a1b565b989b979a50959850939692959293505050565b5f5f60408385031215614d4e575f5ffd5b82359150614d5e60208401614992565b90509250929050565b600781106146c5576146c56146a1565b6020810161396f8284614d67565b5f6101e0828403128015614d97575f5ffd5b509092915050565b5f5f60608385031215614db0575f5ffd5b614db98361463c565b915083606084011115614dca575f5ffd5b50926020919091019150565b828152604060208201525f614dee60408301846147e9565b949350505050565b600181811c90821680614e0a57607f821691505b602082108103610ef157634e487b7160e01b5f52602260045260245ffd5b83815260608101614e3c6020830185614d67565b614dee6040830184614d67565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561396f5761396f614e49565b60408101614e7e8285614d67565b6145fd6020830184614d67565b634e487b7160e01b5f52604160045260245ffd5b601f821115613b3b57805f5260205f20601f840160051c81016020851015614ec45750805b601f840160051c820191505b81811015614ee3575f8155600101614ed0565b5050505050565b5f19600383901b1c191660019190911b1790565b6001600160401b03831115614f1557614f15614e8b565b614f2983614f238354614df6565b83614e9f565b5f601f841160018114614f55575f8515614f435750838201355b614f4d8682614eea565b845550614ee3565b5f83815260208120601f198716915b82811015614f845786850135825560209485019460019092019101614f64565b5086821015614fa0575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b60ff84168152604060208201525f614ff6604083018486614fb2565b95945050505050565b5f8151808452602084019350602083015f5b828110156150385781516001600160a01b0316865260209586019590910190600101615011565b5093949350505050565b848152836020820152608060408201525f6150606080830185614fff565b905060018060a01b038316606083015295945050505050565b634e487b7160e01b5f52603260045260245ffd5b818382375f9101908152919050565b848152836020820152606060408201525f614446606083018486614fb2565b8015158114610acc575f5ffd5b5f602082840312156150d8575f5ffd5b81516145fd816150bb565b602081525f614dee602083018486614fb2565b6020808252601c908201527f42465620706172616d20736574206e6f74207265676973746572656400000000604082015260600190565b5f6020828403121561513d575f5ffd5b6145fd8261463c565b6020810161396f82846146d7565b808202811582820484141761396f5761396f614e49565b5f8261518557634e487b7160e01b5f52601260045260245ffd5b500490565b5f6020828403121561519a575f5ffd5b5051919050565b8181038181111561396f5761396f614e49565b858152606060208201525f6151cd606083018688614fb2565b82810360408401526151e0818587614fb2565b98975050505050505050565b604081525f6151ff604083018688614fb2565b8281036020840152615212818587614fb2565b979650505050505050565b61ffff81168114610acc575f5ffd5b803561464a8161521d565b5f60208284031215615247575f5ffd5b81356145fd8161521d565b63ffffffff81168114610acc575f5ffd5b803561464a81615252565b5f6020828403121561527e575f5ffd5b81356145fd81615252565b5f813561396f816145ce565b5f813561396f8161521d565b5f813561396f81615252565b813581556020820135600182015560408201356002820155606082013560038201556080820135600482015560a0820135600582015560c082013560068201556007810161531d61530060e08501615289565b82546001600160a01b0319166001600160a01b0391909116178255565b61534d61532d6101008501615295565b82805461ffff60a01b191660a09290921b61ffff60a01b16919091179055565b61537d61535d6101208501615295565b82805461ffff60b01b191660b09290921b61ffff60b01b16919091179055565b6153ad61538d6101408501615295565b82805461ffff60c01b191660c09290921b61ffff60c01b16919091179055565b6153dd6153bd6101608501615295565b82805461ffff60d01b191660d09290921b61ffff60d01b16919091179055565b61540d6153ed6101808501615295565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b50600881016154396154226101a085016152a1565b825463ffffffff191663ffffffff91909116178255565b613b3b6154496101c085016152a1565b825467ffffffff00000000191660209190911b67ffffffff0000000016178255565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c080830135908201526101e081016154bc60e08401614901565b6154c960e084018261461b565b506154d7610100840161522c565b61ffff166101008301526154ee610120840161522c565b61ffff16610120830152615505610140840161522c565b61ffff1661014083015261551c610160840161522c565b61ffff16610160830152615533610180840161522c565b61ffff1661018083015261554a6101a08401615263565b63ffffffff166101a08301526155636101c08401615263565b63ffffffff81166101c0840152614c91565b6040810181835f5b60028110156155ac57813561559181615252565b63ffffffff168352602092830192919091019060010161557d565b50505092915050565b5f600182016155c6576155c6614e49565b5060010190565b5f5f8335601e198436030181126155e2575f5ffd5b8301803591506001600160401b038211156155fb575f5ffd5b602001915036819003821315613cc1575f5ffd5b5f6020828403121561561f575f5ffd5b81356145fd816150bb565b87815286602082015260a060408201525f61564860a08301886146e7565b828103606084015261565b818789614fb2565b90508281036080840152615670818587614fb2565b9a9950505050505050505050565b81516001600160401b0381111561569757615697614e8b565b6156ab816156a58454614df6565b84614e9f565b6020601f8211600181146156d8575f83156156c65750848201515b6156d08482614eea565b855550614ee3565b5f84815260208120601f198516915b8281101561570757878501518255602094850194600190920191016156e7565b508482101561572457868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b838152602081018390526080810160408201835f5b600281101561576d57815163ffffffff16835260209283019290910190600101615748565b505050949350505050565b604081016157868285614d67565b6145fd60208301846146b5565b604051601f8201601f191681016001600160401b03811182821017156157bb576157bb614e8b565b604052919050565b5f6001600160401b038211156157db576157db614e8b565b5060051b60200190565b5f82601f8301126157f4575f5ffd5b8151615807615802826157c3565b615793565b8082825260208201915060208360051b860101925085831115615828575f5ffd5b602085015b8381101561584557805183526020928301920161582d565b5095945050505050565b5f5f60408385031215615860575f5ffd5b82516001600160401b03811115615875575f5ffd5b8301601f81018513615885575f5ffd5b8051615893615802826157c3565b8082825260208201915060208360051b8501019250878311156158b4575f5ffd5b6020840193505b828410156158df5783516158ce816145ce565b8252602093840193909101906158bb565b8095505050505060208301516001600160401b038111156158fe575f5ffd5b61590a858286016157e5565b9150509250929050565b6001600160a01b03929092168252602082015260400190565b838152606060208201525f6159456060830185614fff565b905060018060a01b0383166040830152949350505050565b5f8151808452602084019350602083015f5b8281101561503857815186526020958601959091019060010161596f565b6001600160a01b03841681526060602082018190525f906159b090830185614fff565b8281036040840152614446818561595d565b604081525f6159d46040830185614fff565b8281036020840152614ff6818561595d56fe1b418a230a21d37a078bf8f16decbde8ccceacd77159371f62f0d4ea00d19967a164736f6c634300081c000a", + "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b615963806100d65f395ff3fe608060405234801561000f575f5ffd5b50600436106102ce575f3560e01c806390173a4111610182578063cb649617116100e0578063f0691cba1161008f578063f0691cba14610886578063f2fde38b14610899578063f3ceba3a146108ac578063f81b8ef6146108cd578063fad8e111146108e0578063fbdb3237146108f3578063fd2f3d011461091b575f5ffd5b8063cb649617146107ef578063cbd16872146107f8578063cf0f34c41461080b578063cfbdc98d1461081e578063d8afed3e1461084d578063e59e469514610860578063ea71aa5714610873575f5ffd5b80639e57b9341161013c5780639e57b93414610607578063a87f4ab91461061a578063ac3d2f421461076c578063bb2d1b8214610794578063bff232c1146107a7578063c1ab0f1f146107ba578063c4ccafa2146107cd575f5ffd5b806390173a41146105705780639117173c146105855780639231238614610598578063929a8faf146105ab57806399c6679d146105cc5780639c8570c8146105f4575f5ffd5b80635d1684181161022f5780637edcd7ab116101e95780637edcd7ab146104e757806381476ec21461050a578063830d71811461051d57806385814243146105305780638da5cb5b146105435780638dcdd86b1461054b5780638e5ce3ad1461055d575f5ffd5b80635d1684181461047d578063647846a51461049d5780636db5c8fd146104b0578063715018a6146104b95780637c8c3b4d146104c15780637cfa9d74146104d4575f5ffd5b806336c5d38a1161028b57806336c5d38a1461039b5780634017daf0146103ca578063406ed35c146103f75780634147a360146104175780634d600e5d146104445780634e92ec63146104575780634fc772641461046a575f5ffd5b806302a3a9c9146102d25780630ef81b2f146102e757806310bc62811461032557806311bd61d91461034d57806315cce224146103755780631ba7294514610388575b5f5ffd5b6102e56102e03660046145f6565b61092e565b005b61030f6102f5366004614618565b5f908152600960205260409020546001600160a01b031690565b60405161031c919061463c565b60405180910390f35b61030f610333366004614618565b60096020525f90815260409020546001600160a01b031681565b61036061035b366004614663565b6109da565b60405163ffffffff909116815260200161031c565b6102e56103833660046145f6565b610a16565b6102e561039636600461469b565b610abb565b6103bd6103a9366004614618565b5f908152600f602052604090205460ff1690565b60405161031c91906146dd565b6103dd6103d8366004614618565b610acf565b60405161031c9e9d9c9b9a99989796959493929190614729565b61040a610405366004614618565b610c7a565b60405161031c9190614903565b610436610425366004614618565b600c6020525f908152604090205481565b60405190815260200161031c565b6102e5610452366004614920565b610ef7565b6102e5610465366004614618565b611134565b6102e56104783660046145f6565b6111c3565b61049061048b3660046149b6565b611256565b60405161031c91906149cf565b60045461030f906001600160a01b031681565b61043660055481565b6102e56112ed565b6102e56104cf3660046149e1565b611300565b6102e56104e2366004614618565b611389565b6104fa6104f5366004614a4c565b611488565b604051901515815260200161031c565b6102e5610518366004614ac3565b6116de565b6102e561052b366004614ae3565b6117d2565b60015461030f906001600160a01b031681565b61030f6118de565b5f5461030f906001600160a01b031681565b60035461030f906001600160a01b031681565b61057861190c565b60405161031c9190614b31565b6102e5610593366004614618565b611952565b6105786105a6366004614618565b611ac0565b6105be6105b9366004614618565b611b19565b60405161031c929190614b52565b61030f6105da366004614618565b5f908152601060205260409020546001600160a01b031690565b6104fa610602366004614a4c565b611b40565b610436610615366004614b68565b611dd8565b61075f604080516101e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915250604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a0840152640100000000909104166101c082015290565b60405161031c9190614b9f565b61030f61077a366004614618565b5f908152600a60205260409020546001600160a01b031690565b6102e56107a2366004614cac565b6123ad565b6102e56107b53660046145f6565b612469565b6102e56107c8366004614ac3565b612510565b6104fa6107db3660046145f6565b60076020525f908152604090205460ff1681565b61043660065481565b6102e56108063660046149e1565b6125cd565b6102e5610819366004614618565b612687565b61084061082c366004614618565b5f908152600d602052604090205460ff1690565b60405161031c9190614ce6565b6102e561085b366004614cf4565b6126c4565b6102e561086e3660046145f6565b612951565b6102e5610881366004614d0e565b6129eb565b60025461030f906001600160a01b031681565b6102e56108a73660046145f6565b612c98565b6108bf6108ba366004614b68565b612cd2565b60405161031c929190614d45565b6103bd6108db366004614618565b6135b3565b6102e56108ee3660046145f6565b61374d565b61030f610901366004614618565b600a6020525f90815260409020546001600160a01b031681565b6102e56109293660046145f6565b6137e5565b610936613874565b6001600160a01b0381166109915760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964204533526566756e644d616e6167657220616464726573730060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040517f9557d04c1c0b16f93f13b69aed23b3b6ab935bff3c53ac81d17896d3583542ed905f90a250565b6012602052815f5260405f2081600281106109f3575f80fd5b60089182820401919006600402915091509054906101000a900463ffffffff1681565b610a1e613874565b6001600160a01b03811615801590610a4457506004546001600160a01b03828116911614155b8190610a645760405163eddf07f560e01b8152600401610988919061463c565b50600480546001600160a01b0319166001600160a01b0383161790556040517f722ff84c1234b2482061def5c82c6b5080c117b3cbb69d686844a051e4b8e7f390610ab090839061463c565b60405180910390a150565b610ac3613874565b610acc816138a6565b50565b60086020525f9081526040902080546001820154600283015460058401546006850154600786018054959660ff95861696949593946001600160a01b03841694600160a01b90940490931692909190610b2790614d65565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5390614d65565b8015610b9e5780601f10610b7557610100808354040283529160200191610b9e565b820191905f5260205f20905b815481529060010190602001808311610b8157829003601f168201915b50505060088401546009850154600a860154600b870154600c8801805497986001600160a01b03958616989490951696509194509291610bdd90614d65565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0990614d65565b8015610c545780601f10610c2b57610100808354040283529160200191610c54565b820191905f5260205f20905b815481529060010190602001808311610c3757829003601f168201915b505050600d90930154919250506001600160a01b0381169060ff600160a01b909104168e565b610c82614464565b5f8281526008602090815260409182902082516101e08101909352805483526001810154909183019060ff166003811115610cbf57610cbf6146b5565b6003811115610cd057610cd06146b5565b8152600282810154602083015260408051808201808352919093019291600385019182845b815481526020019060010190808311610cf55750505091835250506005820154602082015260068201546001600160a01b0381166040830152600160a01b900460ff166060820152600782018054608090920191610d5290614d65565b80601f0160208091040260200160405190810160405280929190818152602001828054610d7e90614d65565b8015610dc95780601f10610da057610100808354040283529160200191610dc9565b820191905f5260205f20905b815481529060010190602001808311610dac57829003601f168201915b505050918352505060088201546001600160a01b0390811660208301526009830154166040820152600a8201546060820152600b8201546080820152600c8201805460a090920191610e1a90614d65565b80601f0160208091040260200160405190810160405280929190818152602001828054610e4690614d65565b8015610e915780601f10610e6857610100808354040283529160200191610e91565b820191905f5260205f20905b815481529060010190602001808311610e7457829003601f168201915b5050509183525050600d91909101546001600160a01b038082166020840152600160a01b90910460ff16151560409092019190915260a0820151919250839116610ef15760405163cd6f4a4f60e01b815260040161098891815260200190565b50919050565b5f610f00613963565b805490915060ff600160401b82041615906001600160401b03165f81158015610f265750825b90505f826001600160401b03166001148015610f415750303b155b905081158015610f4f575080155b15610f6d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f9757845460ff60401b1916600160401b1785555b610fa03361398d565b610fa987612687565b610fb28b61374d565b610fbb8a612951565b610fc48961092e565b610fcd88610a16565b610fd6866138a6565b604080516101e08101825261c3508082526161a86020808401829052611388948401859052601460608501819052620249f0608086018190526207a12060a087018190526107d060c088018190525f60e089018190526103e86101008a015261012089018190526109c46101408a018190526101608a018b90526101808a01526101a089018190526101c090980197909752601895909555601993909355601a95909555601b94909455601c55601d55601e55601f80546001600160f01b031916690138827101388000007d60a31b179055805467ffffffffffffffff191690556110bf6118de565b6001600160a01b03168c6001600160a01b0316146110e0576110e08c612c98565b831561112657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050565b61113c613874565b5f8181526009602052604090205481906001600160a01b0316611175576040516381c4951960e01b815260040161098891815260200190565b505f818152600960205260409081902080546001600160a01b0319169055517f104eb329a192aef26eddea07c2af5ad2587792e62b37ed4045b6ba59bc5540fc90610ab09083815260200190565b6111cb613874565b6001600160a01b0381165f90815260076020526040902054819060ff16611206576040516321ac7c5f60e01b8152600401610988919061463c565b506001600160a01b0381165f9081526007602052604090819020805460ff19169055517f56070b80bd617fcd2f7a284861edb488830a38f9dedcd77b2cb2f4eac17743e790610ab090839061463c565b600b6020525f90815260409020805461126e90614d65565b80601f016020809104026020016040519081016040528092919081815260200182805461129a90614d65565b80156112e55780601f106112bc576101008083540402835291602001916112e5565b820191905f5260205f20905b8154815290600101906020018083116112c857829003601f168201915b505050505081565b6112f5613874565b6112fe5f61399e565b565b611308613874565b6001600160a01b0381161580159061133957505f828152600a60205260409020546001600160a01b03828116911614155b829061135b576040516381c4951960e01b815260040161098891815260200190565b505f918252600a602052604090912080546001600160a01b0319166001600160a01b03909216919091179055565b5f546001600160a01b031633146113b35760405163b56831db60e01b815260040160405180910390fd5b5f818152600d602052604090205460ff1660018160068111156113d8576113d86146b5565b146113fd57816001826040516337e1404160e01b815260040161098893929190614d97565b5f828152600d60205260409020805460ff191660021790556015546114229042614dcc565b5f838152600e602052604080822092909255905183917fc44405af9078047712501f519e1fb900c2896c62b488336f84529c72ae16e6f191a2815f5160206159375f395f51905f526001600260405161147c929190614ddf565b60405180910390a25050565b5f5f61149387610c7a565b5f888152600d602052604090205490915060ff1660048160068111156114bb576114bb6146b5565b14886004839091926114e3576040516337e1404160e01b815260040161098893929190614d97565b5050505f888152600e60209081526040918290208251606081018452815481526001820154928101929092526002015491810182905290899042811015611546576040516308f3034360e31b815260048101929092526024820152604401610988565b50505f898152600860205260409020600c01611563888a83614e6d565b505f898152600d60205260409020805460ff191660051790556101c08301511561165d57846115a557604051631eae1a4d60e31b815260040160405180910390fd5b8261010001516001600160a01b031663258ae58289896040516115c9929190614f21565b6040519081900381206001600160e01b031960e084901b1682526115f3918a908a90600401614f58565b602060405180830381865afa15801561160e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116329190614f87565b935087878561165657604051632f9f8ab960e01b8152600401610988929190614fa2565b5050611662565b600193505b61166b89613a0e565b887f3a140076c461ebc41d74833ae0ee8bbc8079a135a63392098cd381e84350b69b898989896040516116a19493929190614fb5565b60405180910390a2885f5160206159375f395f51905f52600460056040516116ca929190614ddf565b60405180910390a250505095945050505050565b5f546001600160a01b031633146117085760405163b56831db60e01b815260040160405180910390fd5b5f828152600860209081526040808320600d9092529091205460ff166002816006811115611738576117386146b5565b1461175d57836002826040516337e1404160e01b815260040161098893929190614d97565b5f848152600d6020526040808220805460ff19166003179055600a84018590555185917f11df18edb9bc9cd90a79068e0e208b630202148643d797d6150e7bacb733e63c91a2835f5160206159375f395f51905f52600260036040516117c4929190614ddf565b60405180910390a250505050565b6117da613874565b806118165760405162461bcd60e51b815260206004820152600c60248201526b456d70747920706172616d7360a01b6044820152606401610988565b60ff83165f908152600b60205260409020805461183290614d65565b1590506118815760405162461bcd60e51b815260206004820152601b60248201527f506172616d53657420616c7265616479207265676973746572656400000000006044820152606401610988565b60ff83165f908152600b6020526040902061189d828483614e6d565b507f6e4a4ea7f38fc775e616080b155744337e6216848e886a69c918b4ab84da21958383836040516118d193929190614fe6565b60405180910390a1505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b61192d60405180606001604052805f81526020015f81526020015f81525090565b5060408051606081018252601554815260165460208201526017549181019190915290565b5f818152600d602052604090205460ff166006816006811115611977576119776146b5565b14829061199a57604051637cb2d48360e11b815260040161098891815260200190565b505f828152600c602052604090205482816119cb576040516345ba89d560e11b815260040161098891815260200190565b505f838152600c602052604081208190556119e584613e7a565b5f858152601160205260409020546002549192506001600160a01b0390811691611a129183911685613f67565b60025460405163da19b69760e01b81526001600160a01b039091169063da19b69790611a48908890879087908790600401615045565b5f604051808303815f87803b158015611a5f575f5ffd5b505af1158015611a71573d5f5f3e3d5ffd5b50505050847f5297818f48a66292b8b3e2caab83eec531b669bb20807fd38cf006adb2a07317848451604051611ab1929190918252602082015260400190565b60405180910390a25050505050565b611ae160405180606001604052805f81526020015f81526020015f81525090565b505f908152600e6020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b5f818152600d6020526040812054819060ff16611b368482613fc4565b9250925050915091565b5f5f611b4b87610c7a565b5f888152600d602052604090205490915060ff166003816006811115611b7357611b736146b5565b1488600383909192611b9b576040516337e1404160e01b815260040161098893929190614d97565b5050505f888152600e6020908152604091829020825160608101845281548152600182015492810183905260029091015492810192909252899042811015611bff576040516308f3034360e31b815260048101929092526024820152604401610988565b5050606083015160200151899042811115611c365760405163017e35e560e71b815260048101929092526024820152604401610988565b5050610160830151899015611c6157604051637eb9cea960e11b815260040161098891815260200190565b505f8888604051611c73929190614f21565b60408051918290039091205f8c815260086020908152838220600b01839055600d905291909120805460ff19166004179055601754909150611cb59042614dcc565b5f8b8152600e6020526040908190206002019190915560a08501519051632f0e1bbf60e01b81526001600160a01b0390911690632f0e1bbf90611d02908d9085908c908c90600401615090565b6020604051808303815f875af1158015611d1e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d429190614f87565b9450888886611d6657604051632f9f8ab960e01b8152600401610988929190614fa2565b5050897f7cc27e4a5626cbc4f8ba1a927b0448de55e6a114bc87660331270c5109ade0718a8a604051611d9a929190614fa2565b60405180910390a2895f5160206159375f395f51905f5260036004604051611dc3929190614ddf565b60405180910390a25050505095945050505050565b5f80600b81611ded60a08601608087016149b6565b60ff1660ff1681526020019081526020015f208054611e0b90614d65565b905011611e2a5760405162461bcd60e51b8152600401610988906150af565b5f601281611e3b60208601866150e6565b6003811115611e4c57611e4c6146b5565b6003811115611e5d57611e5d6146b5565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411611e84579050505050505090505f81600160028110611edb57611edb61507c565b602002015163ffffffff1611835f016020810190611ef991906150e6565b90611f185760405163286c068d60e11b815260040161098891906150ff565b506020808201518251604080516101e081018252601854815260195481860152601a5491810191909152601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e0830152600160a01b810461ffff908116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152925463ffffffff8181166101a0860181905264010000000090920481166101c086015292831693919092169115612043576101a081015163ffffffff16846001602002015163ffffffff161015865f01602081019061202291906150e6565b906120415760405163010b971d60e31b815260040161098891906150ff565b505b6101c081015163ffffffff1615612092576101c081015184519063ffffffff908116908216101561209057604051630a4b6b6360e11b815263ffffffff9091166004820152602401610988565b505b604086013560208701358110156120bf5760405163174b5a0760e21b815260040161098891815260200190565b506101808101516017545f91612710916120dd9161ffff169061510d565b6120e79190615124565b61271061ffff1683610160015161ffff16601560010154612108919061510d565b6121129190615124565b61271061ffff1684610140015161ffff1660155f0154612132919061510d565b61213c9190615124565b5f5460408051634f87c3a560e11b8152815160208e81013594938f0135936001600160a01b031692639f0f874a92600480830193928290030181865afa158015612188573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121ac9190615143565b6121b69190614dcc565b6121c0919061515a565b6121ca9190614dcc565b6121d49190614dcc565b6121de9190614dcc565b90505f6121ec60018661515a565b6121f790600261510d565b61220290600261510d565b61220d906006614dcc565b90505f85845f015161221f919061510d565b905081868560200151612232919061510d565b61223c919061510d565b6122469082614dcc565b9050600186111561228e57600261225e60018861515a565b612268908861510d565b8560400151612277919061510d565b6122819190615124565b61228b9082614dcc565b90505b81868560c0015161229f919061510d565b6122a9919061510d565b6122b39082614dcc565b9050828685606001516122c6919061510d565b6122d0919061510d565b6122da9082614dcc565b90508484608001516122ec919061510d565b6122f69082614dcc565b9050600185111561233e57600261230e60018761515a565b612318908761510d565b8560400151612327919061510d565b6123319190615124565b61233b9082614dcc565b90505b60a084015161234d9082614dcc565b610100850151909150612710906123689061ffff1682614dcc565b612372908361510d565b61237c9190615124565b975087806123a057604051638c4fcd9360e01b815260040161098891815260200190565b5050505050505050919050565b5f546001600160a01b03163314806123cf57506003546001600160a01b031633145b6123ec57604051639e75a8b560e01b815260040160405180910390fd5b5f8160ff161180156124025750600d60ff821611155b6124475760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b2103330b4b63ab932903932b0b9b7b760511b6044820152606401610988565b612465828260ff16600d811115612460576124606146b5565b61414c565b5050565b612471613874565b6001600160a01b0381166124c75760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420536c617368696e674d616e616765722061646472657373006044820152606401610988565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6003546001600160a01b0316331461253b576040516357d6948d60e11b815260040160405180910390fd5b60025460405163c1ab0f1f60e01b815260048101849052602481018390526001600160a01b039091169063c1ab0f1f906044015f604051808303815f87803b158015612585575f5ffd5b505af1158015612597573d5f5f3e3d5ffd5b50505050817f4f41a3b0a032ebcae925f2ace77d507435840ca4b2dbaffdd7723fa8d72ee5428260405161147c91815260200190565b6125d5613874565b6001600160a01b0381161580159061260657505f828152600960205260409020546001600160a01b03828116911614155b8290612628576040516381c4951960e01b815260040161098891815260200190565b505f8281526009602090815260409182902080546001600160a01b0319166001600160a01b03851617905590518381527ff4041a3f914dac3bc9bf5f003ba41f28dbb84abe42f4e07c76266f5c8ceecb69910160405180910390a15050565b61268f613874565b60058190556040518181527fba0716ba1ee2ea8ecc4c64119b4537cdb42a99d82acf92af5b87607b8b52355290602001610ab0565b6126cc613874565b6127106126e161012083016101008401615187565b61ffff1611156126f961012083016101008401615187565b9061271e576040516301027fc160e21b815261ffff9091166004820152602401610988565b5061271061273461014083016101208401615187565b61ffff16111561274c61014083016101208401615187565b90612771576040516301027fc160e21b815261ffff9091166004820152602401610988565b5061271061278761016083016101408401615187565b61ffff16111561279f61016083016101408401615187565b906127c457604051633239953960e01b815261ffff9091166004820152602401610988565b506127106127da61018083016101608401615187565b61ffff1611156127f261018083016101608401615187565b9061281757604051633239953960e01b815261ffff9091166004820152602401610988565b5061271061282d6101a083016101808401615187565b61ffff1611156128456101a083016101808401615187565b9061286a57604051633239953960e01b815261ffff9091166004820152602401610988565b5061287d61014082016101208301615187565b61ffff1615806128a657505f61289a610100830160e084016145f6565b6001600160a01b031614155b6128c35760405163015f92ff60e51b815260040160405180910390fd5b6128d56101e082016101c083016151be565b63ffffffff166128ed6101c083016101a084016151be565b63ffffffff161015612912576040516392f55c6560e01b815260040160405180910390fd5b80601861291f82826151fd565b9050507fbf3951313e980027eb48ce363fdb707286195ec6a0f802ac153927cf929c3fc681604051610ab091906153bb565b612959613874565b6001600160a01b0381161580159061297f57506001546001600160a01b03828116911614155b819061299f576040516320252f0b60e01b8152600401610988919061463c565b50600180546001600160a01b0319166001600160a01b0383161790556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790610ab090839061463c565b6129f3613874565b612a0060208201826151be565b63ffffffff16612a1660408301602084016151be565b63ffffffff1610158015612a3b57505f612a3360208301836151be565b63ffffffff16115b612a5857604051634564ab9b60e01b815260040160405180910390fd5b604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a08401819052640100000000909204166101c083015215612b91576101a081015163ffffffff16612b4160408401602085016151be565b63ffffffff161015612b5960408401602085016151be565b826101a001519091612b8e57604051633ccc4c2160e21b815263ffffffff928316600482015291166024820152604401610988565b50505b6101c081015163ffffffff1615612c08576101c081015163ffffffff16612bbb60208401846151be565b63ffffffff161015612bd060208401846151be565b826101c001519091612c055760405163156c4e5b60e11b815263ffffffff928316600482015291166024820152604401610988565b50505b8160125f856003811115612c1e57612c1e6146b5565b6003811115612c2f57612c2f6146b5565b815260208101919091526040015f20612c499160026144e1565b50826003811115612c5c57612c5c6146b5565b7f8b56fae526eee054f0849759a99fc7d4ff3823824ebf097a56f7d78adb6b34fa83604051612c8b91906154c5565b60405180910390a2505050565b612ca0613874565b6001600160a01b038116612cc9575f604051631e4fbdf760e01b8152600401610988919061463c565b610acc8161399e565b5f612cdb614464565b5f601281612cec60208701876150e6565b6003811115612cfd57612cfd6146b5565b6003811115612d0e57612d0e6146b5565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612d35579050505050505090505f81600160028110612d8c57612d8c61507c565b602002015163ffffffff1611845f016020810190612daa91906150e6565b90612dc95760405163286c068d60e11b815260040161098891906150ff565b50602084013542811015612df357604051630b99e87960e01b815260040161098891815260200190565b5060408401356020850135811015612e215760405163174b5a0760e21b815260040161098891815260200190565b506017546016545f9190612e3942604089013561515a565b612e439190614dcc565b612e4d9190614dcc565b905060055481108190612e76576040516313b783af60e21b815260040161098891815260200190565b5060075f612e8a60808801606089016145f6565b6001600160a01b0316815260208101919091526040015f205460ff16612eb660808701606088016145f6565b90612ed55760405163295a6a6f60e11b8152600401610988919061463c565b505f612ee086611dd8565b60068054965090915085905f612ef583615505565b9091555050604080514460208201529081018690525f9060600160408051601f1981840301815291815281516020928301205f898152600c84528281208690556004546011855283822080546001600160a01b03199081166001600160a01b0393841617909155601f805460138852868520805461ffff191661ffff600160b01b909304929092169190911790555460148752858420805483169190931617909155600d8552838220805460ff191660011790556010909452829020805433941693909317909255601654919250612fd09190890135614dcc565b5f878152600e6020908152604090912060010191909155818652612ff6908801886150e6565b8560200190600381111561300c5761300c6146b5565b9081600381111561301f5761301f6146b5565b905250436040808701919091528051808201825290602089019060029083908390808284375f92019190915250505060608087019190915261306790608089019089016145f6565b6001600160a01b031660a080870191909152613088908801608089016149b6565b60ff1660c0808701919091526130a09088018861551d565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060e0808701919091526130ed90610100890190890161555f565b15156101c08601525f610140860181905261016086018190526040805160208101909152908152610180860152336101a0860181905260045461313d916001600160a01b039091169030856142a7565b5f600b8161315160a08b0160808c016149b6565b60ff1660ff1681526020019081526020015f20805461316f90614d65565b80601f016020809104026020016040519081016040528092919081815260200182805461319b90614d65565b80156131e65780601f106131bd576101008083540402835291602001916131e6565b820191905f5260205f20905b8154815290600101906020018083116131c957829003601f168201915b505050505090505f81511161320d5760405162461bcd60e51b8152600401610988906150af565b5f61321e60808a0160608b016145f6565b6001600160a01b031663fefd9a8b89858561323c60a08f018f61551d565b8f8060c0019061324c919061551d565b6040518863ffffffff1660e01b815260040161326e979695949392919061557a565b6020604051808303815f875af115801561328a573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132ae9190615143565b5f818152600960205260409020549091506001600160a01b031681816132ea576040516381c4951960e01b815260040161098891815260200190565b505f828152600a60205260409020546001600160a01b03168281613324576040516381c4951960e01b815260040161098891815260200190565b50608089018390526001600160a01b038083166101008b015281166101208a01525f8a81526008602090815260409091208a518155908a0151600180830180548d94939260ff199190911690836003811115613382576133826146b5565b0217905550604082015181600201556060820151816003019060026133a8929190614582565b506080820151600582015560a082015160068201805460c085015160ff16600160a01b026001600160a81b03199091166001600160a01b039093169290921791909117905560e0820151600782019061340190826155ce565b506101008201516008820180546001600160a01b039283166001600160a01b031991821617909155610120840151600984018054919093169116179055610140820151600a820155610160820151600b820155610180820151600c82019061346990826155ce565b506101a0820151600d90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091555f5460405163291a691b60e01b815291169063291a691b906134cf908d9089908d90600401615683565b6020604051808303815f875af11580156134eb573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061350f9190614f87565b61352c57604051630d8dbe2560e01b815260040160405180910390fd5b61353c60808c0160608d016145f6565b6001600160a01b03167f5090c9764b5cd13df7afc0013f733dfbe6eaf1b6ddc22a5e291fa387efd4c15e8b8b604051613576929190614d45565b60405180910390a2895f5160206159375f395f51905f525f600160405161359e929190614ddf565b60405180910390a25050505050505050915091565b5f818152600d602052604081205460ff16818160068111156135d7576135d76146b5565b036135fc57826001826040516337e1404160e01b815260040161098893929190614d97565b6005816006811115613610576136106146b5565b036136315760405163462c7bed60e01b815260048101849052602401610988565b6006816006811115613645576136456146b5565b0361366657604051633de16e3560e11b815260048101849052602401610988565b5f6136718483613fc4565b935090508061369657604051639f65d93560e01b815260048101859052602401610988565b5f848152600d6020526040902080546006919060ff191660018302179055505f848152600f60205260409020805484919060ff1916600183600d8111156136df576136df6146b5565b0217905550835f5160206159375f395f51905f52836006604051613704929190614ddf565b60405180910390a2837fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb838560405161373e9291906156c8565b60405180910390a25050919050565b613755613874565b6001600160a01b0381161580159061377a57505f546001600160a01b03828116911614155b819061379a576040516375ac4eb760e11b8152600401610988919061463c565b505f80546001600160a01b0319166001600160a01b0383161790556040517f80052b810d39120cf6c976cca504a21703f585521dc7a41c6d241090e6c579b690610ab090839061463c565b6001600160a01b0381165f90815260076020526040902054819060ff16156138215760405163b29d459560e01b8152600401610988919061463c565b506001600160a01b0381165f9081526007602052604090819020805460ff19166001179055517fb8d368517268f297fff00825d67d098763117d061360d31027be5b2e1a59d46790610ab090839061463c565b3361387d6118de565b6001600160a01b0316146112fe573360405163118cdaa760e01b8152600401610988919061463c565b80356138c55760405163055f269d60e01b815260040160405180910390fd5b5f8160200135116138e95760405163055f269d60e01b815260040160405180910390fd5b5f81604001351161390d5760405163055f269d60e01b815260040160405180910390fd5b80356015819055602080830135601681905560408085013560178190558151948552928401919091528201527f7e86ba16b805e2835af5c5b7aa5a942ced8bcc1fb95a05fbe42dae3862350a1690606001610ab0565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b6139956142e6565b610acc8161430b565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f805460405162beb08960e51b8152600481018490526001600160a01b03909116906317d61120906024015f60405180830381865afa158015613a53573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613a7a919081019061579f565b5080515f848152600c60209081526040808320805490849055601190925282205493945091926001600160a01b031690829003613b19576002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613ae590889088908690600401615864565b5f604051808303815f87803b158015613afc575f5ffd5b505af1158015613b0e573d5f5f3e3d5ffd5b505050505050505050565b825f03613bba575f858152601060205260409020546001600160a01b03168015613b5157613b516001600160a01b0383168285613f67565b6002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613b8590899089908790600401615864565b5f604051808303815f87803b158015613b9c575f5ffd5b505af1158015613bae573d5f5f3e3d5ffd5b50505050505050505050565b5f85815260136020908152604080832054601490925282205461ffff909116906001600160a01b03168115801590613bfa57506001600160a01b03811615155b15613c3657612710613c1061ffff84168761510d565b613c1a9190615124565b92508215613c3657613c366001600160a01b0385168285613f67565b5f613c41848761515a565b90505f876001600160401b03811115613c5c57613c5c614dfa565b604051908082528060200260200182016040528015613c85578160200160208202803683370190505b5090505f613c938984615124565b90505f805b8a811015613cd25782848281518110613cb357613cb361507c565b6020908102919091010152613cc88383614dcc565b9150600101613c98565b505f613cde828661515a565b90508015613d1b578084613cf360018e61515a565b81518110613d0357613d0361507c565b60200260200101818151613d179190614dcc565b9052505b600154613d35906001600160a01b038b8116911687614313565b60015f9054906101000a90046001600160a01b03166001600160a01b031663dd8c818e8a8e876040518463ffffffff1660e01b8152600401613d79939291906158c4565b5f604051808303815f87803b158015613d90575f5ffd5b505af1158015613da2573d5f5f3e3d5ffd5b5050600154613dc092506001600160a01b038c81169250165f614313565b8c7fac9fe8ad7f55eac03284399116ecafc104f10459773f4cdf47063c46e5be335a8d86604051613df29291906158f9565b60405180910390a260025f9054906101000a90046001600160a01b03166001600160a01b03166341489f158e8e8c6040518463ffffffff1660e01b8152600401613e3e93929190615864565b5f604051808303815f87803b158015613e55575f5ffd5b505af1158015613e67573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f818152600f602052604090205460609060ff16600181600d811115613ea257613ea26146b5565b1480613ebf5750600281600d811115613ebd57613ebd6146b5565b145b15613ef7575f5b604051908082528060200260200182016040528015613eef578160200160208202803683370190505b509392505050565b5f5460405162beb08960e51b8152600481018590526001600160a01b03909116906317d61120906024015f60405180830381865afa925050508015613f5d57506040513d5f823e601f3d908101601f19168201604052613f5a919081019061579f565b60015b613eef575f613ec6565b613fbf83846001600160a01b031663a9059cbb8585604051602401613f8d92919061591d565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061439f565b505050565b5f828152600e60209081526040808320815160608101835281548152600182015493810193909352600201548282015282549051632800d82960e01b81526004810186905283929183916001600160a01b0390911690632800d82990602401602060405180830381865afa15801561403e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906140629190615143565b90506001856006811115614078576140786146b5565b14801561408457508042115b1561409757600180935093505050614145565b60028560068111156140ab576140ab6146b5565b1480156140b85750815142115b156140cc5760016003935093505050614145565b60038560068111156140e0576140e06146b5565b1480156140f05750816020015142115b156141045760016006935093505050614145565b6004856006811115614118576141186146b5565b1480156141285750816040015142115b1561413c576001600a935093505050614145565b5f5f9350935050505b9250929050565b5f828152600d602052604081205460ff1690816006811115614170576141706146b5565b0361419557826001826040516337e1404160e01b815260040161098893929190614d97565b60058160068111156141a9576141a96146b5565b036141ca5760405163462c7bed60e01b815260048101849052602401610988565b60068160068111156141de576141de6146b5565b036141ff57604051633de16e3560e11b815260048101849052602401610988565b5f838152600d6020526040902080546006919060ff191660018302179055505f838152600f60205260409020805483919060ff1916600183600d811115614248576142486146b5565b0217905550825f5160206159375f395f51905f5282600660405161426d929190614ddf565b60405180910390a2827fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb8284604051612c8b9291906156c8565b6040516001600160a01b0384811660248301528381166044830152606482018390526142e09186918216906323b872dd90608401613f8d565b50505050565b6142ee614402565b6112fe57604051631afcd79f60e31b815260040160405180910390fd5b612ca06142e6565b5f836001600160a01b031663095ea7b3848460405160240161433692919061591d565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050905061436f848261441b565b6142e05761439984856001600160a01b031663095ea7b3865f604051602401613f8d92919061591d565b6142e084825b5f5f60205f8451602086015f885af1806143be576040513d5f823e3d81fd5b50505f513d915081156143d55780600114156143e2565b6001600160a01b0384163b155b156142e05783604051635274afe760e01b8152600401610988919061463c565b5f61440b613963565b54600160401b900460ff16919050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f51905082801561445a5750811561444c578060011461445a565b5f866001600160a01b03163b115b9695505050505050565b604080516101e081019091525f808252602082019081526020015f815260200161448c6145b0565b81525f602082018190526040820181905260608083018290526080830181905260a0830182905260c0830182905260e08301829052610100830182905261012083015261014082018190526101609091015290565b600183019183908215614572579160200282015f5b8382111561454057833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026144f6565b80156145705782816101000a81549063ffffffff0219169055600401602081600301049283019260010302614540565b505b5061457e9291506145ce565b5090565b8260028101928215614572579160200282015b82811115614572578251825591602001919060010190614595565b60405180604001604052806002906020820280368337509192915050565b5b8082111561457e575f81556001016145cf565b6001600160a01b0381168114610acc575f5ffd5b5f60208284031215614606575f5ffd5b8135614611816145e2565b9392505050565b5f60208284031215614628575f5ffd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b80356004811061465e575f5ffd5b919050565b5f5f60408385031215614674575f5ffd5b61467d83614650565b946020939093013593505050565b5f60608284031215610ef1575f5ffd5b5f606082840312156146ab575f5ffd5b614611838361468b565b634e487b7160e01b5f52602160045260245ffd5b600e81106146d9576146d96146b5565b9052565b6020810161398782846146c9565b600481106146d9576146d96146b5565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8e8152614739602082018f6146eb565b8c60408201528b6060820152614752608082018c61462f565b60ff8a1660a08201526101c060c08201525f6147726101c083018b6146fb565b61477f60e084018b61462f565b61478d61010084018a61462f565b87610120840152866101408401528281036101608401526147ae81876146fb565b9150506147bf61018083018561462f565b8215156101a08301529f9e505050505050505050505050505050565b805f5b60028110156142e05781518452602093840193909101906001016147de565b805182525f602082015161481460208501826146eb565b5060408201516040840152606082015161483160608501826147db565b50608082015160a084015260a082015161484e60c085018261462f565b5060c082015160ff811660e08501525060e082015161020061010085015261487a6102008501826146fb565b905061010083015161489061012086018261462f565b506101208301516148a561014086018261462f565b506101408301516101608501526101608301516101808501526101808301518482036101a08601526148d782826146fb565b9150506101a08301516148ee6101c086018261462f565b506101c08301518015156101e0860152613eef565b602081525f61461160208301846147fd565b803561465e816145e2565b5f5f5f5f5f5f5f610120888a031215614937575f5ffd5b8735614942816145e2565b96506020880135614952816145e2565b95506040880135614962816145e2565b94506060880135614972816145e2565b93506080880135614982816145e2565b925060a088013591506149988960c08a0161468b565b905092959891949750929550565b803560ff8116811461465e575f5ffd5b5f602082840312156149c6575f5ffd5b614611826149a6565b602081525f61461160208301846146fb565b5f5f604083850312156149f2575f5ffd5b823591506020830135614a04816145e2565b809150509250929050565b5f5f83601f840112614a1f575f5ffd5b5081356001600160401b03811115614a35575f5ffd5b602083019150836020828501011115614145575f5ffd5b5f5f5f5f5f60608688031215614a60575f5ffd5b8535945060208601356001600160401b03811115614a7c575f5ffd5b614a8888828901614a0f565b90955093505060408601356001600160401b03811115614aa6575f5ffd5b614ab288828901614a0f565b969995985093965092949392505050565b5f5f60408385031215614ad4575f5ffd5b50508035926020909101359150565b5f5f5f60408486031215614af5575f5ffd5b614afe846149a6565b925060208401356001600160401b03811115614b18575f5ffd5b614b2486828701614a0f565b9497909650939450505050565b81518152602080830151908201526040808301519082015260608101613987565b82151581526040810161461160208301846146c9565b5f60208284031215614b78575f5ffd5b81356001600160401b03811115614b8d575f5ffd5b82016101008185031215614611575f5ffd5b5f6101e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e0830151614bf960e084018261462f565b50610100830151614c1161010084018261ffff169052565b50610120830151614c2961012084018261ffff169052565b50610140830151614c4161014084018261ffff169052565b50610160830151614c5961016084018261ffff169052565b50610180830151614c7161018084018261ffff169052565b506101a0830151614c8b6101a084018263ffffffff169052565b506101c0830151614ca56101c084018263ffffffff169052565b5092915050565b5f5f60408385031215614cbd575f5ffd5b82359150614ccd602084016149a6565b90509250929050565b600781106146d9576146d96146b5565b602081016139878284614cd6565b5f6101e0828403128015614d06575f5ffd5b509092915050565b5f5f60608385031215614d1f575f5ffd5b614d2883614650565b915083606084011115614d39575f5ffd5b50926020919091019150565b828152604060208201525f614d5d60408301846147fd565b949350505050565b600181811c90821680614d7957607f821691505b602082108103610ef157634e487b7160e01b5f52602260045260245ffd5b83815260608101614dab6020830185614cd6565b614d5d6040830184614cd6565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561398757613987614db8565b60408101614ded8285614cd6565b6146116020830184614cd6565b634e487b7160e01b5f52604160045260245ffd5b601f821115613fbf57805f5260205f20601f840160051c81016020851015614e335750805b601f840160051c820191505b81811015614e52575f8155600101614e3f565b5050505050565b5f19600383901b1c191660019190911b1790565b6001600160401b03831115614e8457614e84614dfa565b614e9883614e928354614d65565b83614e0e565b5f601f841160018114614ec4575f8515614eb25750838201355b614ebc8682614e59565b845550614e52565b5f83815260208120601f198716915b82811015614ef35786850135825560209485019460019092019101614ed3565b5086821015614f0f575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b818382375f9101908152919050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b838152604060208201525f614f71604083018486614f30565b95945050505050565b8015158114610acc575f5ffd5b5f60208284031215614f97575f5ffd5b815161461181614f7a565b602081525f614d5d602083018486614f30565b604081525f614fc8604083018688614f30565b8281036020840152614fdb818587614f30565b979650505050505050565b60ff84168152604060208201525f614f71604083018486614f30565b5f8151808452602084019350602083015f5b8281101561503b5781516001600160a01b0316865260209586019590910190600101615014565b5093949350505050565b848152836020820152608060408201525f6150636080830185615002565b905060018060a01b038316606083015295945050505050565b634e487b7160e01b5f52603260045260245ffd5b848152836020820152606060408201525f61445a606083018486614f30565b6020808252601c908201527f42465620706172616d20736574206e6f74207265676973746572656400000000604082015260600190565b5f602082840312156150f6575f5ffd5b61461182614650565b6020810161398782846146eb565b808202811582820484141761398757613987614db8565b5f8261513e57634e487b7160e01b5f52601260045260245ffd5b500490565b5f60208284031215615153575f5ffd5b5051919050565b8181038181111561398757613987614db8565b61ffff81168114610acc575f5ffd5b803561465e8161516d565b5f60208284031215615197575f5ffd5b81356146118161516d565b63ffffffff81168114610acc575f5ffd5b803561465e816151a2565b5f602082840312156151ce575f5ffd5b8135614611816151a2565b5f8135613987816145e2565b5f81356139878161516d565b5f8135613987816151a2565b813581556020820135600182015560408201356002820155606082013560038201556080820135600482015560a0820135600582015560c082013560068201556007810161526d61525060e085016151d9565b82546001600160a01b0319166001600160a01b0391909116178255565b61529d61527d61010085016151e5565b82805461ffff60a01b191660a09290921b61ffff60a01b16919091179055565b6152cd6152ad61012085016151e5565b82805461ffff60b01b191660b09290921b61ffff60b01b16919091179055565b6152fd6152dd61014085016151e5565b82805461ffff60c01b191660c09290921b61ffff60c01b16919091179055565b61532d61530d61016085016151e5565b82805461ffff60d01b191660d09290921b61ffff60d01b16919091179055565b61535d61533d61018085016151e5565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b50600881016153896153726101a085016151f1565b825463ffffffff191663ffffffff91909116178255565b613fbf6153996101c085016151f1565b825467ffffffff00000000191660209190911b67ffffffff0000000016178255565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c080830135908201526101e0810161540c60e08401614915565b61541960e084018261462f565b50615427610100840161517c565b61ffff1661010083015261543e610120840161517c565b61ffff16610120830152615455610140840161517c565b61ffff1661014083015261546c610160840161517c565b61ffff16610160830152615483610180840161517c565b61ffff1661018083015261549a6101a084016151b3565b63ffffffff166101a08301526154b36101c084016151b3565b63ffffffff81166101c0840152614ca5565b6040810181835f5b60028110156154fc5781356154e1816151a2565b63ffffffff16835260209283019291909101906001016154cd565b50505092915050565b5f6001820161551657615516614db8565b5060010190565b5f5f8335601e19843603018112615532575f5ffd5b8301803591506001600160401b0382111561554b575f5ffd5b602001915036819003821315614145575f5ffd5b5f6020828403121561556f575f5ffd5b813561461181614f7a565b87815286602082015260a060408201525f61559860a08301886146fb565b82810360608401526155ab818789614f30565b905082810360808401526155c0818587614f30565b9a9950505050505050505050565b81516001600160401b038111156155e7576155e7614dfa565b6155fb816155f58454614d65565b84614e0e565b6020601f821160018114615628575f83156156165750848201515b6156208482614e59565b855550614e52565b5f84815260208120601f198516915b828110156156575787850151825560209485019460019092019101615637565b508482101561567457868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b838152602081018390526080810160408201835f5b60028110156156bd57815163ffffffff16835260209283019290910190600101615698565b505050949350505050565b604081016156d68285614cd6565b61461160208301846146c9565b604051601f8201601f191681016001600160401b038111828210171561570b5761570b614dfa565b604052919050565b5f6001600160401b0382111561572b5761572b614dfa565b5060051b60200190565b5f82601f830112615744575f5ffd5b815161575761575282615713565b6156e3565b8082825260208201915060208360051b860101925085831115615778575f5ffd5b602085015b8381101561579557805183526020928301920161577d565b5095945050505050565b5f5f604083850312156157b0575f5ffd5b82516001600160401b038111156157c5575f5ffd5b8301601f810185136157d5575f5ffd5b80516157e361575282615713565b8082825260208201915060208360051b850101925087831115615804575f5ffd5b6020840193505b8284101561582f57835161581e816145e2565b82526020938401939091019061580b565b8095505050505060208301516001600160401b0381111561584e575f5ffd5b61585a85828601615735565b9150509250929050565b838152606060208201525f61587c6060830185615002565b905060018060a01b0383166040830152949350505050565b5f8151808452602084019350602083015f5b8281101561503b5781518652602095860195909101906001016158a6565b6001600160a01b03841681526060602082018190525f906158e790830185615002565b828103604084015261445a8185615894565b604081525f61590b6040830185615002565b8281036020840152614f718185615894565b6001600160a01b0392909216825260208201526040019056fe1b418a230a21d37a078bf8f16decbde8ccceacd77159371f62f0d4ea00d19967a164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b50600436106102ce575f3560e01c806390173a4111610182578063cb649617116100e0578063f0691cba1161008f578063f0691cba14610886578063f2fde38b14610899578063f3ceba3a146108ac578063f81b8ef6146108cd578063fad8e111146108e0578063fbdb3237146108f3578063fd2f3d011461091b575f5ffd5b8063cb649617146107ef578063cbd16872146107f8578063cf0f34c41461080b578063cfbdc98d1461081e578063d8afed3e1461084d578063e59e469514610860578063ea71aa5714610873575f5ffd5b80639e57b9341161013c5780639e57b93414610607578063a87f4ab91461061a578063ac3d2f421461076c578063bb2d1b8214610794578063bff232c1146107a7578063c1ab0f1f146107ba578063c4ccafa2146107cd575f5ffd5b806390173a41146105705780639117173c146105855780639231238614610598578063929a8faf146105ab57806399c6679d146105cc5780639c8570c8146105f4575f5ffd5b80635d1684181161022f5780637edcd7ab116101e95780637edcd7ab146104e757806381476ec21461050a578063830d71811461051d57806385814243146105305780638da5cb5b146105435780638dcdd86b1461054b5780638e5ce3ad1461055d575f5ffd5b80635d1684181461047d578063647846a51461049d5780636db5c8fd146104b0578063715018a6146104b95780637c8c3b4d146104c15780637cfa9d74146104d4575f5ffd5b806336c5d38a1161028b57806336c5d38a1461039b5780634017daf0146103ca578063406ed35c146103f75780634147a360146104175780634d600e5d146104445780634e92ec63146104575780634fc772641461046a575f5ffd5b806302a3a9c9146102d25780630ef81b2f146102e757806310bc62811461032557806311bd61d91461034d57806315cce224146103755780631ba7294514610388575b5f5ffd5b6102e56102e03660046145f6565b61092e565b005b61030f6102f5366004614618565b5f908152600960205260409020546001600160a01b031690565b60405161031c919061463c565b60405180910390f35b61030f610333366004614618565b60096020525f90815260409020546001600160a01b031681565b61036061035b366004614663565b6109da565b60405163ffffffff909116815260200161031c565b6102e56103833660046145f6565b610a16565b6102e561039636600461469b565b610abb565b6103bd6103a9366004614618565b5f908152600f602052604090205460ff1690565b60405161031c91906146dd565b6103dd6103d8366004614618565b610acf565b60405161031c9e9d9c9b9a99989796959493929190614729565b61040a610405366004614618565b610c7a565b60405161031c9190614903565b610436610425366004614618565b600c6020525f908152604090205481565b60405190815260200161031c565b6102e5610452366004614920565b610ef7565b6102e5610465366004614618565b611134565b6102e56104783660046145f6565b6111c3565b61049061048b3660046149b6565b611256565b60405161031c91906149cf565b60045461030f906001600160a01b031681565b61043660055481565b6102e56112ed565b6102e56104cf3660046149e1565b611300565b6102e56104e2366004614618565b611389565b6104fa6104f5366004614a4c565b611488565b604051901515815260200161031c565b6102e5610518366004614ac3565b6116de565b6102e561052b366004614ae3565b6117d2565b60015461030f906001600160a01b031681565b61030f6118de565b5f5461030f906001600160a01b031681565b60035461030f906001600160a01b031681565b61057861190c565b60405161031c9190614b31565b6102e5610593366004614618565b611952565b6105786105a6366004614618565b611ac0565b6105be6105b9366004614618565b611b19565b60405161031c929190614b52565b61030f6105da366004614618565b5f908152601060205260409020546001600160a01b031690565b6104fa610602366004614a4c565b611b40565b610436610615366004614b68565b611dd8565b61075f604080516101e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915250604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a0840152640100000000909104166101c082015290565b60405161031c9190614b9f565b61030f61077a366004614618565b5f908152600a60205260409020546001600160a01b031690565b6102e56107a2366004614cac565b6123ad565b6102e56107b53660046145f6565b612469565b6102e56107c8366004614ac3565b612510565b6104fa6107db3660046145f6565b60076020525f908152604090205460ff1681565b61043660065481565b6102e56108063660046149e1565b6125cd565b6102e5610819366004614618565b612687565b61084061082c366004614618565b5f908152600d602052604090205460ff1690565b60405161031c9190614ce6565b6102e561085b366004614cf4565b6126c4565b6102e561086e3660046145f6565b612951565b6102e5610881366004614d0e565b6129eb565b60025461030f906001600160a01b031681565b6102e56108a73660046145f6565b612c98565b6108bf6108ba366004614b68565b612cd2565b60405161031c929190614d45565b6103bd6108db366004614618565b6135b3565b6102e56108ee3660046145f6565b61374d565b61030f610901366004614618565b600a6020525f90815260409020546001600160a01b031681565b6102e56109293660046145f6565b6137e5565b610936613874565b6001600160a01b0381166109915760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964204533526566756e644d616e6167657220616464726573730060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040517f9557d04c1c0b16f93f13b69aed23b3b6ab935bff3c53ac81d17896d3583542ed905f90a250565b6012602052815f5260405f2081600281106109f3575f80fd5b60089182820401919006600402915091509054906101000a900463ffffffff1681565b610a1e613874565b6001600160a01b03811615801590610a4457506004546001600160a01b03828116911614155b8190610a645760405163eddf07f560e01b8152600401610988919061463c565b50600480546001600160a01b0319166001600160a01b0383161790556040517f722ff84c1234b2482061def5c82c6b5080c117b3cbb69d686844a051e4b8e7f390610ab090839061463c565b60405180910390a150565b610ac3613874565b610acc816138a6565b50565b60086020525f9081526040902080546001820154600283015460058401546006850154600786018054959660ff95861696949593946001600160a01b03841694600160a01b90940490931692909190610b2790614d65565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5390614d65565b8015610b9e5780601f10610b7557610100808354040283529160200191610b9e565b820191905f5260205f20905b815481529060010190602001808311610b8157829003601f168201915b50505060088401546009850154600a860154600b870154600c8801805497986001600160a01b03958616989490951696509194509291610bdd90614d65565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0990614d65565b8015610c545780601f10610c2b57610100808354040283529160200191610c54565b820191905f5260205f20905b815481529060010190602001808311610c3757829003601f168201915b505050600d90930154919250506001600160a01b0381169060ff600160a01b909104168e565b610c82614464565b5f8281526008602090815260409182902082516101e08101909352805483526001810154909183019060ff166003811115610cbf57610cbf6146b5565b6003811115610cd057610cd06146b5565b8152600282810154602083015260408051808201808352919093019291600385019182845b815481526020019060010190808311610cf55750505091835250506005820154602082015260068201546001600160a01b0381166040830152600160a01b900460ff166060820152600782018054608090920191610d5290614d65565b80601f0160208091040260200160405190810160405280929190818152602001828054610d7e90614d65565b8015610dc95780601f10610da057610100808354040283529160200191610dc9565b820191905f5260205f20905b815481529060010190602001808311610dac57829003601f168201915b505050918352505060088201546001600160a01b0390811660208301526009830154166040820152600a8201546060820152600b8201546080820152600c8201805460a090920191610e1a90614d65565b80601f0160208091040260200160405190810160405280929190818152602001828054610e4690614d65565b8015610e915780601f10610e6857610100808354040283529160200191610e91565b820191905f5260205f20905b815481529060010190602001808311610e7457829003601f168201915b5050509183525050600d91909101546001600160a01b038082166020840152600160a01b90910460ff16151560409092019190915260a0820151919250839116610ef15760405163cd6f4a4f60e01b815260040161098891815260200190565b50919050565b5f610f00613963565b805490915060ff600160401b82041615906001600160401b03165f81158015610f265750825b90505f826001600160401b03166001148015610f415750303b155b905081158015610f4f575080155b15610f6d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f9757845460ff60401b1916600160401b1785555b610fa03361398d565b610fa987612687565b610fb28b61374d565b610fbb8a612951565b610fc48961092e565b610fcd88610a16565b610fd6866138a6565b604080516101e08101825261c3508082526161a86020808401829052611388948401859052601460608501819052620249f0608086018190526207a12060a087018190526107d060c088018190525f60e089018190526103e86101008a015261012089018190526109c46101408a018190526101608a018b90526101808a01526101a089018190526101c090980197909752601895909555601993909355601a95909555601b94909455601c55601d55601e55601f80546001600160f01b031916690138827101388000007d60a31b179055805467ffffffffffffffff191690556110bf6118de565b6001600160a01b03168c6001600160a01b0316146110e0576110e08c612c98565b831561112657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050565b61113c613874565b5f8181526009602052604090205481906001600160a01b0316611175576040516381c4951960e01b815260040161098891815260200190565b505f818152600960205260409081902080546001600160a01b0319169055517f104eb329a192aef26eddea07c2af5ad2587792e62b37ed4045b6ba59bc5540fc90610ab09083815260200190565b6111cb613874565b6001600160a01b0381165f90815260076020526040902054819060ff16611206576040516321ac7c5f60e01b8152600401610988919061463c565b506001600160a01b0381165f9081526007602052604090819020805460ff19169055517f56070b80bd617fcd2f7a284861edb488830a38f9dedcd77b2cb2f4eac17743e790610ab090839061463c565b600b6020525f90815260409020805461126e90614d65565b80601f016020809104026020016040519081016040528092919081815260200182805461129a90614d65565b80156112e55780601f106112bc576101008083540402835291602001916112e5565b820191905f5260205f20905b8154815290600101906020018083116112c857829003601f168201915b505050505081565b6112f5613874565b6112fe5f61399e565b565b611308613874565b6001600160a01b0381161580159061133957505f828152600a60205260409020546001600160a01b03828116911614155b829061135b576040516381c4951960e01b815260040161098891815260200190565b505f918252600a602052604090912080546001600160a01b0319166001600160a01b03909216919091179055565b5f546001600160a01b031633146113b35760405163b56831db60e01b815260040160405180910390fd5b5f818152600d602052604090205460ff1660018160068111156113d8576113d86146b5565b146113fd57816001826040516337e1404160e01b815260040161098893929190614d97565b5f828152600d60205260409020805460ff191660021790556015546114229042614dcc565b5f838152600e602052604080822092909255905183917fc44405af9078047712501f519e1fb900c2896c62b488336f84529c72ae16e6f191a2815f5160206159375f395f51905f526001600260405161147c929190614ddf565b60405180910390a25050565b5f5f61149387610c7a565b5f888152600d602052604090205490915060ff1660048160068111156114bb576114bb6146b5565b14886004839091926114e3576040516337e1404160e01b815260040161098893929190614d97565b5050505f888152600e60209081526040918290208251606081018452815481526001820154928101929092526002015491810182905290899042811015611546576040516308f3034360e31b815260048101929092526024820152604401610988565b50505f898152600860205260409020600c01611563888a83614e6d565b505f898152600d60205260409020805460ff191660051790556101c08301511561165d57846115a557604051631eae1a4d60e31b815260040160405180910390fd5b8261010001516001600160a01b031663258ae58289896040516115c9929190614f21565b6040519081900381206001600160e01b031960e084901b1682526115f3918a908a90600401614f58565b602060405180830381865afa15801561160e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116329190614f87565b935087878561165657604051632f9f8ab960e01b8152600401610988929190614fa2565b5050611662565b600193505b61166b89613a0e565b887f3a140076c461ebc41d74833ae0ee8bbc8079a135a63392098cd381e84350b69b898989896040516116a19493929190614fb5565b60405180910390a2885f5160206159375f395f51905f52600460056040516116ca929190614ddf565b60405180910390a250505095945050505050565b5f546001600160a01b031633146117085760405163b56831db60e01b815260040160405180910390fd5b5f828152600860209081526040808320600d9092529091205460ff166002816006811115611738576117386146b5565b1461175d57836002826040516337e1404160e01b815260040161098893929190614d97565b5f848152600d6020526040808220805460ff19166003179055600a84018590555185917f11df18edb9bc9cd90a79068e0e208b630202148643d797d6150e7bacb733e63c91a2835f5160206159375f395f51905f52600260036040516117c4929190614ddf565b60405180910390a250505050565b6117da613874565b806118165760405162461bcd60e51b815260206004820152600c60248201526b456d70747920706172616d7360a01b6044820152606401610988565b60ff83165f908152600b60205260409020805461183290614d65565b1590506118815760405162461bcd60e51b815260206004820152601b60248201527f506172616d53657420616c7265616479207265676973746572656400000000006044820152606401610988565b60ff83165f908152600b6020526040902061189d828483614e6d565b507f6e4a4ea7f38fc775e616080b155744337e6216848e886a69c918b4ab84da21958383836040516118d193929190614fe6565b60405180910390a1505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b61192d60405180606001604052805f81526020015f81526020015f81525090565b5060408051606081018252601554815260165460208201526017549181019190915290565b5f818152600d602052604090205460ff166006816006811115611977576119776146b5565b14829061199a57604051637cb2d48360e11b815260040161098891815260200190565b505f828152600c602052604090205482816119cb576040516345ba89d560e11b815260040161098891815260200190565b505f838152600c602052604081208190556119e584613e7a565b5f858152601160205260409020546002549192506001600160a01b0390811691611a129183911685613f67565b60025460405163da19b69760e01b81526001600160a01b039091169063da19b69790611a48908890879087908790600401615045565b5f604051808303815f87803b158015611a5f575f5ffd5b505af1158015611a71573d5f5f3e3d5ffd5b50505050847f5297818f48a66292b8b3e2caab83eec531b669bb20807fd38cf006adb2a07317848451604051611ab1929190918252602082015260400190565b60405180910390a25050505050565b611ae160405180606001604052805f81526020015f81526020015f81525090565b505f908152600e6020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b5f818152600d6020526040812054819060ff16611b368482613fc4565b9250925050915091565b5f5f611b4b87610c7a565b5f888152600d602052604090205490915060ff166003816006811115611b7357611b736146b5565b1488600383909192611b9b576040516337e1404160e01b815260040161098893929190614d97565b5050505f888152600e6020908152604091829020825160608101845281548152600182015492810183905260029091015492810192909252899042811015611bff576040516308f3034360e31b815260048101929092526024820152604401610988565b5050606083015160200151899042811115611c365760405163017e35e560e71b815260048101929092526024820152604401610988565b5050610160830151899015611c6157604051637eb9cea960e11b815260040161098891815260200190565b505f8888604051611c73929190614f21565b60408051918290039091205f8c815260086020908152838220600b01839055600d905291909120805460ff19166004179055601754909150611cb59042614dcc565b5f8b8152600e6020526040908190206002019190915560a08501519051632f0e1bbf60e01b81526001600160a01b0390911690632f0e1bbf90611d02908d9085908c908c90600401615090565b6020604051808303815f875af1158015611d1e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d429190614f87565b9450888886611d6657604051632f9f8ab960e01b8152600401610988929190614fa2565b5050897f7cc27e4a5626cbc4f8ba1a927b0448de55e6a114bc87660331270c5109ade0718a8a604051611d9a929190614fa2565b60405180910390a2895f5160206159375f395f51905f5260036004604051611dc3929190614ddf565b60405180910390a25050505095945050505050565b5f80600b81611ded60a08601608087016149b6565b60ff1660ff1681526020019081526020015f208054611e0b90614d65565b905011611e2a5760405162461bcd60e51b8152600401610988906150af565b5f601281611e3b60208601866150e6565b6003811115611e4c57611e4c6146b5565b6003811115611e5d57611e5d6146b5565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411611e84579050505050505090505f81600160028110611edb57611edb61507c565b602002015163ffffffff1611835f016020810190611ef991906150e6565b90611f185760405163286c068d60e11b815260040161098891906150ff565b506020808201518251604080516101e081018252601854815260195481860152601a5491810191909152601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e0830152600160a01b810461ffff908116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152925463ffffffff8181166101a0860181905264010000000090920481166101c086015292831693919092169115612043576101a081015163ffffffff16846001602002015163ffffffff161015865f01602081019061202291906150e6565b906120415760405163010b971d60e31b815260040161098891906150ff565b505b6101c081015163ffffffff1615612092576101c081015184519063ffffffff908116908216101561209057604051630a4b6b6360e11b815263ffffffff9091166004820152602401610988565b505b604086013560208701358110156120bf5760405163174b5a0760e21b815260040161098891815260200190565b506101808101516017545f91612710916120dd9161ffff169061510d565b6120e79190615124565b61271061ffff1683610160015161ffff16601560010154612108919061510d565b6121129190615124565b61271061ffff1684610140015161ffff1660155f0154612132919061510d565b61213c9190615124565b5f5460408051634f87c3a560e11b8152815160208e81013594938f0135936001600160a01b031692639f0f874a92600480830193928290030181865afa158015612188573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121ac9190615143565b6121b69190614dcc565b6121c0919061515a565b6121ca9190614dcc565b6121d49190614dcc565b6121de9190614dcc565b90505f6121ec60018661515a565b6121f790600261510d565b61220290600261510d565b61220d906006614dcc565b90505f85845f015161221f919061510d565b905081868560200151612232919061510d565b61223c919061510d565b6122469082614dcc565b9050600186111561228e57600261225e60018861515a565b612268908861510d565b8560400151612277919061510d565b6122819190615124565b61228b9082614dcc565b90505b81868560c0015161229f919061510d565b6122a9919061510d565b6122b39082614dcc565b9050828685606001516122c6919061510d565b6122d0919061510d565b6122da9082614dcc565b90508484608001516122ec919061510d565b6122f69082614dcc565b9050600185111561233e57600261230e60018761515a565b612318908761510d565b8560400151612327919061510d565b6123319190615124565b61233b9082614dcc565b90505b60a084015161234d9082614dcc565b610100850151909150612710906123689061ffff1682614dcc565b612372908361510d565b61237c9190615124565b975087806123a057604051638c4fcd9360e01b815260040161098891815260200190565b5050505050505050919050565b5f546001600160a01b03163314806123cf57506003546001600160a01b031633145b6123ec57604051639e75a8b560e01b815260040160405180910390fd5b5f8160ff161180156124025750600d60ff821611155b6124475760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b2103330b4b63ab932903932b0b9b7b760511b6044820152606401610988565b612465828260ff16600d811115612460576124606146b5565b61414c565b5050565b612471613874565b6001600160a01b0381166124c75760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420536c617368696e674d616e616765722061646472657373006044820152606401610988565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6003546001600160a01b0316331461253b576040516357d6948d60e11b815260040160405180910390fd5b60025460405163c1ab0f1f60e01b815260048101849052602481018390526001600160a01b039091169063c1ab0f1f906044015f604051808303815f87803b158015612585575f5ffd5b505af1158015612597573d5f5f3e3d5ffd5b50505050817f4f41a3b0a032ebcae925f2ace77d507435840ca4b2dbaffdd7723fa8d72ee5428260405161147c91815260200190565b6125d5613874565b6001600160a01b0381161580159061260657505f828152600960205260409020546001600160a01b03828116911614155b8290612628576040516381c4951960e01b815260040161098891815260200190565b505f8281526009602090815260409182902080546001600160a01b0319166001600160a01b03851617905590518381527ff4041a3f914dac3bc9bf5f003ba41f28dbb84abe42f4e07c76266f5c8ceecb69910160405180910390a15050565b61268f613874565b60058190556040518181527fba0716ba1ee2ea8ecc4c64119b4537cdb42a99d82acf92af5b87607b8b52355290602001610ab0565b6126cc613874565b6127106126e161012083016101008401615187565b61ffff1611156126f961012083016101008401615187565b9061271e576040516301027fc160e21b815261ffff9091166004820152602401610988565b5061271061273461014083016101208401615187565b61ffff16111561274c61014083016101208401615187565b90612771576040516301027fc160e21b815261ffff9091166004820152602401610988565b5061271061278761016083016101408401615187565b61ffff16111561279f61016083016101408401615187565b906127c457604051633239953960e01b815261ffff9091166004820152602401610988565b506127106127da61018083016101608401615187565b61ffff1611156127f261018083016101608401615187565b9061281757604051633239953960e01b815261ffff9091166004820152602401610988565b5061271061282d6101a083016101808401615187565b61ffff1611156128456101a083016101808401615187565b9061286a57604051633239953960e01b815261ffff9091166004820152602401610988565b5061287d61014082016101208301615187565b61ffff1615806128a657505f61289a610100830160e084016145f6565b6001600160a01b031614155b6128c35760405163015f92ff60e51b815260040160405180910390fd5b6128d56101e082016101c083016151be565b63ffffffff166128ed6101c083016101a084016151be565b63ffffffff161015612912576040516392f55c6560e01b815260040160405180910390fd5b80601861291f82826151fd565b9050507fbf3951313e980027eb48ce363fdb707286195ec6a0f802ac153927cf929c3fc681604051610ab091906153bb565b612959613874565b6001600160a01b0381161580159061297f57506001546001600160a01b03828116911614155b819061299f576040516320252f0b60e01b8152600401610988919061463c565b50600180546001600160a01b0319166001600160a01b0383161790556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790610ab090839061463c565b6129f3613874565b612a0060208201826151be565b63ffffffff16612a1660408301602084016151be565b63ffffffff1610158015612a3b57505f612a3360208301836151be565b63ffffffff16115b612a5857604051634564ab9b60e01b815260040160405180910390fd5b604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a08401819052640100000000909204166101c083015215612b91576101a081015163ffffffff16612b4160408401602085016151be565b63ffffffff161015612b5960408401602085016151be565b826101a001519091612b8e57604051633ccc4c2160e21b815263ffffffff928316600482015291166024820152604401610988565b50505b6101c081015163ffffffff1615612c08576101c081015163ffffffff16612bbb60208401846151be565b63ffffffff161015612bd060208401846151be565b826101c001519091612c055760405163156c4e5b60e11b815263ffffffff928316600482015291166024820152604401610988565b50505b8160125f856003811115612c1e57612c1e6146b5565b6003811115612c2f57612c2f6146b5565b815260208101919091526040015f20612c499160026144e1565b50826003811115612c5c57612c5c6146b5565b7f8b56fae526eee054f0849759a99fc7d4ff3823824ebf097a56f7d78adb6b34fa83604051612c8b91906154c5565b60405180910390a2505050565b612ca0613874565b6001600160a01b038116612cc9575f604051631e4fbdf760e01b8152600401610988919061463c565b610acc8161399e565b5f612cdb614464565b5f601281612cec60208701876150e6565b6003811115612cfd57612cfd6146b5565b6003811115612d0e57612d0e6146b5565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612d35579050505050505090505f81600160028110612d8c57612d8c61507c565b602002015163ffffffff1611845f016020810190612daa91906150e6565b90612dc95760405163286c068d60e11b815260040161098891906150ff565b50602084013542811015612df357604051630b99e87960e01b815260040161098891815260200190565b5060408401356020850135811015612e215760405163174b5a0760e21b815260040161098891815260200190565b506017546016545f9190612e3942604089013561515a565b612e439190614dcc565b612e4d9190614dcc565b905060055481108190612e76576040516313b783af60e21b815260040161098891815260200190565b5060075f612e8a60808801606089016145f6565b6001600160a01b0316815260208101919091526040015f205460ff16612eb660808701606088016145f6565b90612ed55760405163295a6a6f60e11b8152600401610988919061463c565b505f612ee086611dd8565b60068054965090915085905f612ef583615505565b9091555050604080514460208201529081018690525f9060600160408051601f1981840301815291815281516020928301205f898152600c84528281208690556004546011855283822080546001600160a01b03199081166001600160a01b0393841617909155601f805460138852868520805461ffff191661ffff600160b01b909304929092169190911790555460148752858420805483169190931617909155600d8552838220805460ff191660011790556010909452829020805433941693909317909255601654919250612fd09190890135614dcc565b5f878152600e6020908152604090912060010191909155818652612ff6908801886150e6565b8560200190600381111561300c5761300c6146b5565b9081600381111561301f5761301f6146b5565b905250436040808701919091528051808201825290602089019060029083908390808284375f92019190915250505060608087019190915261306790608089019089016145f6565b6001600160a01b031660a080870191909152613088908801608089016149b6565b60ff1660c0808701919091526130a09088018861551d565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060e0808701919091526130ed90610100890190890161555f565b15156101c08601525f610140860181905261016086018190526040805160208101909152908152610180860152336101a0860181905260045461313d916001600160a01b039091169030856142a7565b5f600b8161315160a08b0160808c016149b6565b60ff1660ff1681526020019081526020015f20805461316f90614d65565b80601f016020809104026020016040519081016040528092919081815260200182805461319b90614d65565b80156131e65780601f106131bd576101008083540402835291602001916131e6565b820191905f5260205f20905b8154815290600101906020018083116131c957829003601f168201915b505050505090505f81511161320d5760405162461bcd60e51b8152600401610988906150af565b5f61321e60808a0160608b016145f6565b6001600160a01b031663fefd9a8b89858561323c60a08f018f61551d565b8f8060c0019061324c919061551d565b6040518863ffffffff1660e01b815260040161326e979695949392919061557a565b6020604051808303815f875af115801561328a573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132ae9190615143565b5f818152600960205260409020549091506001600160a01b031681816132ea576040516381c4951960e01b815260040161098891815260200190565b505f828152600a60205260409020546001600160a01b03168281613324576040516381c4951960e01b815260040161098891815260200190565b50608089018390526001600160a01b038083166101008b015281166101208a01525f8a81526008602090815260409091208a518155908a0151600180830180548d94939260ff199190911690836003811115613382576133826146b5565b0217905550604082015181600201556060820151816003019060026133a8929190614582565b506080820151600582015560a082015160068201805460c085015160ff16600160a01b026001600160a81b03199091166001600160a01b039093169290921791909117905560e0820151600782019061340190826155ce565b506101008201516008820180546001600160a01b039283166001600160a01b031991821617909155610120840151600984018054919093169116179055610140820151600a820155610160820151600b820155610180820151600c82019061346990826155ce565b506101a0820151600d90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091555f5460405163291a691b60e01b815291169063291a691b906134cf908d9089908d90600401615683565b6020604051808303815f875af11580156134eb573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061350f9190614f87565b61352c57604051630d8dbe2560e01b815260040160405180910390fd5b61353c60808c0160608d016145f6565b6001600160a01b03167f5090c9764b5cd13df7afc0013f733dfbe6eaf1b6ddc22a5e291fa387efd4c15e8b8b604051613576929190614d45565b60405180910390a2895f5160206159375f395f51905f525f600160405161359e929190614ddf565b60405180910390a25050505050505050915091565b5f818152600d602052604081205460ff16818160068111156135d7576135d76146b5565b036135fc57826001826040516337e1404160e01b815260040161098893929190614d97565b6005816006811115613610576136106146b5565b036136315760405163462c7bed60e01b815260048101849052602401610988565b6006816006811115613645576136456146b5565b0361366657604051633de16e3560e11b815260048101849052602401610988565b5f6136718483613fc4565b935090508061369657604051639f65d93560e01b815260048101859052602401610988565b5f848152600d6020526040902080546006919060ff191660018302179055505f848152600f60205260409020805484919060ff1916600183600d8111156136df576136df6146b5565b0217905550835f5160206159375f395f51905f52836006604051613704929190614ddf565b60405180910390a2837fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb838560405161373e9291906156c8565b60405180910390a25050919050565b613755613874565b6001600160a01b0381161580159061377a57505f546001600160a01b03828116911614155b819061379a576040516375ac4eb760e11b8152600401610988919061463c565b505f80546001600160a01b0319166001600160a01b0383161790556040517f80052b810d39120cf6c976cca504a21703f585521dc7a41c6d241090e6c579b690610ab090839061463c565b6001600160a01b0381165f90815260076020526040902054819060ff16156138215760405163b29d459560e01b8152600401610988919061463c565b506001600160a01b0381165f9081526007602052604090819020805460ff19166001179055517fb8d368517268f297fff00825d67d098763117d061360d31027be5b2e1a59d46790610ab090839061463c565b3361387d6118de565b6001600160a01b0316146112fe573360405163118cdaa760e01b8152600401610988919061463c565b80356138c55760405163055f269d60e01b815260040160405180910390fd5b5f8160200135116138e95760405163055f269d60e01b815260040160405180910390fd5b5f81604001351161390d5760405163055f269d60e01b815260040160405180910390fd5b80356015819055602080830135601681905560408085013560178190558151948552928401919091528201527f7e86ba16b805e2835af5c5b7aa5a942ced8bcc1fb95a05fbe42dae3862350a1690606001610ab0565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b6139956142e6565b610acc8161430b565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f805460405162beb08960e51b8152600481018490526001600160a01b03909116906317d61120906024015f60405180830381865afa158015613a53573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613a7a919081019061579f565b5080515f848152600c60209081526040808320805490849055601190925282205493945091926001600160a01b031690829003613b19576002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613ae590889088908690600401615864565b5f604051808303815f87803b158015613afc575f5ffd5b505af1158015613b0e573d5f5f3e3d5ffd5b505050505050505050565b825f03613bba575f858152601060205260409020546001600160a01b03168015613b5157613b516001600160a01b0383168285613f67565b6002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613b8590899089908790600401615864565b5f604051808303815f87803b158015613b9c575f5ffd5b505af1158015613bae573d5f5f3e3d5ffd5b50505050505050505050565b5f85815260136020908152604080832054601490925282205461ffff909116906001600160a01b03168115801590613bfa57506001600160a01b03811615155b15613c3657612710613c1061ffff84168761510d565b613c1a9190615124565b92508215613c3657613c366001600160a01b0385168285613f67565b5f613c41848761515a565b90505f876001600160401b03811115613c5c57613c5c614dfa565b604051908082528060200260200182016040528015613c85578160200160208202803683370190505b5090505f613c938984615124565b90505f805b8a811015613cd25782848281518110613cb357613cb361507c565b6020908102919091010152613cc88383614dcc565b9150600101613c98565b505f613cde828661515a565b90508015613d1b578084613cf360018e61515a565b81518110613d0357613d0361507c565b60200260200101818151613d179190614dcc565b9052505b600154613d35906001600160a01b038b8116911687614313565b60015f9054906101000a90046001600160a01b03166001600160a01b031663dd8c818e8a8e876040518463ffffffff1660e01b8152600401613d79939291906158c4565b5f604051808303815f87803b158015613d90575f5ffd5b505af1158015613da2573d5f5f3e3d5ffd5b5050600154613dc092506001600160a01b038c81169250165f614313565b8c7fac9fe8ad7f55eac03284399116ecafc104f10459773f4cdf47063c46e5be335a8d86604051613df29291906158f9565b60405180910390a260025f9054906101000a90046001600160a01b03166001600160a01b03166341489f158e8e8c6040518463ffffffff1660e01b8152600401613e3e93929190615864565b5f604051808303815f87803b158015613e55575f5ffd5b505af1158015613e67573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f818152600f602052604090205460609060ff16600181600d811115613ea257613ea26146b5565b1480613ebf5750600281600d811115613ebd57613ebd6146b5565b145b15613ef7575f5b604051908082528060200260200182016040528015613eef578160200160208202803683370190505b509392505050565b5f5460405162beb08960e51b8152600481018590526001600160a01b03909116906317d61120906024015f60405180830381865afa925050508015613f5d57506040513d5f823e601f3d908101601f19168201604052613f5a919081019061579f565b60015b613eef575f613ec6565b613fbf83846001600160a01b031663a9059cbb8585604051602401613f8d92919061591d565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061439f565b505050565b5f828152600e60209081526040808320815160608101835281548152600182015493810193909352600201548282015282549051632800d82960e01b81526004810186905283929183916001600160a01b0390911690632800d82990602401602060405180830381865afa15801561403e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906140629190615143565b90506001856006811115614078576140786146b5565b14801561408457508042115b1561409757600180935093505050614145565b60028560068111156140ab576140ab6146b5565b1480156140b85750815142115b156140cc5760016003935093505050614145565b60038560068111156140e0576140e06146b5565b1480156140f05750816020015142115b156141045760016006935093505050614145565b6004856006811115614118576141186146b5565b1480156141285750816040015142115b1561413c576001600a935093505050614145565b5f5f9350935050505b9250929050565b5f828152600d602052604081205460ff1690816006811115614170576141706146b5565b0361419557826001826040516337e1404160e01b815260040161098893929190614d97565b60058160068111156141a9576141a96146b5565b036141ca5760405163462c7bed60e01b815260048101849052602401610988565b60068160068111156141de576141de6146b5565b036141ff57604051633de16e3560e11b815260048101849052602401610988565b5f838152600d6020526040902080546006919060ff191660018302179055505f838152600f60205260409020805483919060ff1916600183600d811115614248576142486146b5565b0217905550825f5160206159375f395f51905f5282600660405161426d929190614ddf565b60405180910390a2827fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb8284604051612c8b9291906156c8565b6040516001600160a01b0384811660248301528381166044830152606482018390526142e09186918216906323b872dd90608401613f8d565b50505050565b6142ee614402565b6112fe57604051631afcd79f60e31b815260040160405180910390fd5b612ca06142e6565b5f836001600160a01b031663095ea7b3848460405160240161433692919061591d565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050905061436f848261441b565b6142e05761439984856001600160a01b031663095ea7b3865f604051602401613f8d92919061591d565b6142e084825b5f5f60205f8451602086015f885af1806143be576040513d5f823e3d81fd5b50505f513d915081156143d55780600114156143e2565b6001600160a01b0384163b155b156142e05783604051635274afe760e01b8152600401610988919061463c565b5f61440b613963565b54600160401b900460ff16919050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f51905082801561445a5750811561444c578060011461445a565b5f866001600160a01b03163b115b9695505050505050565b604080516101e081019091525f808252602082019081526020015f815260200161448c6145b0565b81525f602082018190526040820181905260608083018290526080830181905260a0830182905260c0830182905260e08301829052610100830182905261012083015261014082018190526101609091015290565b600183019183908215614572579160200282015f5b8382111561454057833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026144f6565b80156145705782816101000a81549063ffffffff0219169055600401602081600301049283019260010302614540565b505b5061457e9291506145ce565b5090565b8260028101928215614572579160200282015b82811115614572578251825591602001919060010190614595565b60405180604001604052806002906020820280368337509192915050565b5b8082111561457e575f81556001016145cf565b6001600160a01b0381168114610acc575f5ffd5b5f60208284031215614606575f5ffd5b8135614611816145e2565b9392505050565b5f60208284031215614628575f5ffd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b80356004811061465e575f5ffd5b919050565b5f5f60408385031215614674575f5ffd5b61467d83614650565b946020939093013593505050565b5f60608284031215610ef1575f5ffd5b5f606082840312156146ab575f5ffd5b614611838361468b565b634e487b7160e01b5f52602160045260245ffd5b600e81106146d9576146d96146b5565b9052565b6020810161398782846146c9565b600481106146d9576146d96146b5565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8e8152614739602082018f6146eb565b8c60408201528b6060820152614752608082018c61462f565b60ff8a1660a08201526101c060c08201525f6147726101c083018b6146fb565b61477f60e084018b61462f565b61478d61010084018a61462f565b87610120840152866101408401528281036101608401526147ae81876146fb565b9150506147bf61018083018561462f565b8215156101a08301529f9e505050505050505050505050505050565b805f5b60028110156142e05781518452602093840193909101906001016147de565b805182525f602082015161481460208501826146eb565b5060408201516040840152606082015161483160608501826147db565b50608082015160a084015260a082015161484e60c085018261462f565b5060c082015160ff811660e08501525060e082015161020061010085015261487a6102008501826146fb565b905061010083015161489061012086018261462f565b506101208301516148a561014086018261462f565b506101408301516101608501526101608301516101808501526101808301518482036101a08601526148d782826146fb565b9150506101a08301516148ee6101c086018261462f565b506101c08301518015156101e0860152613eef565b602081525f61461160208301846147fd565b803561465e816145e2565b5f5f5f5f5f5f5f610120888a031215614937575f5ffd5b8735614942816145e2565b96506020880135614952816145e2565b95506040880135614962816145e2565b94506060880135614972816145e2565b93506080880135614982816145e2565b925060a088013591506149988960c08a0161468b565b905092959891949750929550565b803560ff8116811461465e575f5ffd5b5f602082840312156149c6575f5ffd5b614611826149a6565b602081525f61461160208301846146fb565b5f5f604083850312156149f2575f5ffd5b823591506020830135614a04816145e2565b809150509250929050565b5f5f83601f840112614a1f575f5ffd5b5081356001600160401b03811115614a35575f5ffd5b602083019150836020828501011115614145575f5ffd5b5f5f5f5f5f60608688031215614a60575f5ffd5b8535945060208601356001600160401b03811115614a7c575f5ffd5b614a8888828901614a0f565b90955093505060408601356001600160401b03811115614aa6575f5ffd5b614ab288828901614a0f565b969995985093965092949392505050565b5f5f60408385031215614ad4575f5ffd5b50508035926020909101359150565b5f5f5f60408486031215614af5575f5ffd5b614afe846149a6565b925060208401356001600160401b03811115614b18575f5ffd5b614b2486828701614a0f565b9497909650939450505050565b81518152602080830151908201526040808301519082015260608101613987565b82151581526040810161461160208301846146c9565b5f60208284031215614b78575f5ffd5b81356001600160401b03811115614b8d575f5ffd5b82016101008185031215614611575f5ffd5b5f6101e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e0830151614bf960e084018261462f565b50610100830151614c1161010084018261ffff169052565b50610120830151614c2961012084018261ffff169052565b50610140830151614c4161014084018261ffff169052565b50610160830151614c5961016084018261ffff169052565b50610180830151614c7161018084018261ffff169052565b506101a0830151614c8b6101a084018263ffffffff169052565b506101c0830151614ca56101c084018263ffffffff169052565b5092915050565b5f5f60408385031215614cbd575f5ffd5b82359150614ccd602084016149a6565b90509250929050565b600781106146d9576146d96146b5565b602081016139878284614cd6565b5f6101e0828403128015614d06575f5ffd5b509092915050565b5f5f60608385031215614d1f575f5ffd5b614d2883614650565b915083606084011115614d39575f5ffd5b50926020919091019150565b828152604060208201525f614d5d60408301846147fd565b949350505050565b600181811c90821680614d7957607f821691505b602082108103610ef157634e487b7160e01b5f52602260045260245ffd5b83815260608101614dab6020830185614cd6565b614d5d6040830184614cd6565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561398757613987614db8565b60408101614ded8285614cd6565b6146116020830184614cd6565b634e487b7160e01b5f52604160045260245ffd5b601f821115613fbf57805f5260205f20601f840160051c81016020851015614e335750805b601f840160051c820191505b81811015614e52575f8155600101614e3f565b5050505050565b5f19600383901b1c191660019190911b1790565b6001600160401b03831115614e8457614e84614dfa565b614e9883614e928354614d65565b83614e0e565b5f601f841160018114614ec4575f8515614eb25750838201355b614ebc8682614e59565b845550614e52565b5f83815260208120601f198716915b82811015614ef35786850135825560209485019460019092019101614ed3565b5086821015614f0f575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b818382375f9101908152919050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b838152604060208201525f614f71604083018486614f30565b95945050505050565b8015158114610acc575f5ffd5b5f60208284031215614f97575f5ffd5b815161461181614f7a565b602081525f614d5d602083018486614f30565b604081525f614fc8604083018688614f30565b8281036020840152614fdb818587614f30565b979650505050505050565b60ff84168152604060208201525f614f71604083018486614f30565b5f8151808452602084019350602083015f5b8281101561503b5781516001600160a01b0316865260209586019590910190600101615014565b5093949350505050565b848152836020820152608060408201525f6150636080830185615002565b905060018060a01b038316606083015295945050505050565b634e487b7160e01b5f52603260045260245ffd5b848152836020820152606060408201525f61445a606083018486614f30565b6020808252601c908201527f42465620706172616d20736574206e6f74207265676973746572656400000000604082015260600190565b5f602082840312156150f6575f5ffd5b61461182614650565b6020810161398782846146eb565b808202811582820484141761398757613987614db8565b5f8261513e57634e487b7160e01b5f52601260045260245ffd5b500490565b5f60208284031215615153575f5ffd5b5051919050565b8181038181111561398757613987614db8565b61ffff81168114610acc575f5ffd5b803561465e8161516d565b5f60208284031215615197575f5ffd5b81356146118161516d565b63ffffffff81168114610acc575f5ffd5b803561465e816151a2565b5f602082840312156151ce575f5ffd5b8135614611816151a2565b5f8135613987816145e2565b5f81356139878161516d565b5f8135613987816151a2565b813581556020820135600182015560408201356002820155606082013560038201556080820135600482015560a0820135600582015560c082013560068201556007810161526d61525060e085016151d9565b82546001600160a01b0319166001600160a01b0391909116178255565b61529d61527d61010085016151e5565b82805461ffff60a01b191660a09290921b61ffff60a01b16919091179055565b6152cd6152ad61012085016151e5565b82805461ffff60b01b191660b09290921b61ffff60b01b16919091179055565b6152fd6152dd61014085016151e5565b82805461ffff60c01b191660c09290921b61ffff60c01b16919091179055565b61532d61530d61016085016151e5565b82805461ffff60d01b191660d09290921b61ffff60d01b16919091179055565b61535d61533d61018085016151e5565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b50600881016153896153726101a085016151f1565b825463ffffffff191663ffffffff91909116178255565b613fbf6153996101c085016151f1565b825467ffffffff00000000191660209190911b67ffffffff0000000016178255565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c080830135908201526101e0810161540c60e08401614915565b61541960e084018261462f565b50615427610100840161517c565b61ffff1661010083015261543e610120840161517c565b61ffff16610120830152615455610140840161517c565b61ffff1661014083015261546c610160840161517c565b61ffff16610160830152615483610180840161517c565b61ffff1661018083015261549a6101a084016151b3565b63ffffffff166101a08301526154b36101c084016151b3565b63ffffffff81166101c0840152614ca5565b6040810181835f5b60028110156154fc5781356154e1816151a2565b63ffffffff16835260209283019291909101906001016154cd565b50505092915050565b5f6001820161551657615516614db8565b5060010190565b5f5f8335601e19843603018112615532575f5ffd5b8301803591506001600160401b0382111561554b575f5ffd5b602001915036819003821315614145575f5ffd5b5f6020828403121561556f575f5ffd5b813561461181614f7a565b87815286602082015260a060408201525f61559860a08301886146fb565b82810360608401526155ab818789614f30565b905082810360808401526155c0818587614f30565b9a9950505050505050505050565b81516001600160401b038111156155e7576155e7614dfa565b6155fb816155f58454614d65565b84614e0e565b6020601f821160018114615628575f83156156165750848201515b6156208482614e59565b855550614e52565b5f84815260208120601f198516915b828110156156575787850151825560209485019460019092019101615637565b508482101561567457868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b838152602081018390526080810160408201835f5b60028110156156bd57815163ffffffff16835260209283019290910190600101615698565b505050949350505050565b604081016156d68285614cd6565b61461160208301846146c9565b604051601f8201601f191681016001600160401b038111828210171561570b5761570b614dfa565b604052919050565b5f6001600160401b0382111561572b5761572b614dfa565b5060051b60200190565b5f82601f830112615744575f5ffd5b815161575761575282615713565b6156e3565b8082825260208201915060208360051b860101925085831115615778575f5ffd5b602085015b8381101561579557805183526020928301920161577d565b5095945050505050565b5f5f604083850312156157b0575f5ffd5b82516001600160401b038111156157c5575f5ffd5b8301601f810185136157d5575f5ffd5b80516157e361575282615713565b8082825260208201915060208360051b850101925087831115615804575f5ffd5b6020840193505b8284101561582f57835161581e816145e2565b82526020938401939091019061580b565b8095505050505060208301516001600160401b0381111561584e575f5ffd5b61585a85828601615735565b9150509250929050565b838152606060208201525f61587c6060830185615002565b905060018060a01b0383166040830152949350505050565b5f8151808452602084019350602083015f5b8281101561503b5781518652602095860195909101906001016158a6565b6001600160a01b03841681526060602082018190525f906158e790830185615002565b828103604084015261445a8185615894565b604081525f61590b6040830185615002565b8281036020840152614f718185615894565b6001600160a01b0392909216825260208201526040019056fe1b418a230a21d37a078bf8f16decbde8ccceacd77159371f62f0d4ea00d19967a164736f6c634300081c000a", "linkReferences": {}, "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-38a8aa7c2958101f3ea7c42a19f4351c1d072b39" + "buildInfoId": "solc-0_8_28-64228f31c3990e4616cf0578598d186612e83409" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index 739fb88b9e..6c074722e3 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -940,5 +940,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-a5591f76a7a8353e3d0dd83e83c16479678f73a9" + "buildInfoId": "solc-0_8_28-64228f31c3990e4616cf0578598d186612e83409" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index 1cc20d8da0..a428dccd8a 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -334,6 +334,12 @@ "name": "publicKey", "type": "bytes" }, + { + "indexed": false, + "internalType": "bytes32", + "name": "pkCommitment", + "type": "bytes32" + }, { "indexed": false, "internalType": "bytes", @@ -786,13 +792,13 @@ "type": "bytes" }, { - "internalType": "bytes", - "name": "proof", - "type": "bytes" + "internalType": "bytes32", + "name": "pkCommitment", + "type": "bytes32" }, { "internalType": "bytes", - "name": "foldProof", + "name": "proof", "type": "bytes" } ], @@ -965,5 +971,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-a5591f76a7a8353e3d0dd83e83c16479678f73a9" + "buildInfoId": "solc-0_8_28-64228f31c3990e4616cf0578598d186612e83409" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index 7596014063..2d85e9e8c1 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -398,6 +398,11 @@ "name": "PlaintextOutputAlreadyPublished", "type": "error" }, + { + "inputs": [], + "name": "ProofRequired", + "type": "error" + }, { "inputs": [ { @@ -1671,11 +1676,6 @@ "internalType": "bytes", "name": "proof", "type": "bytes" - }, - { - "internalType": "bytes", - "name": "foldProof", - "type": "bytes" } ], "name": "publishPlaintextOutput", @@ -2078,5 +2078,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-a5591f76a7a8353e3d0dd83e83c16479678f73a9" + "buildInfoId": "solc-0_8_28-64228f31c3990e4616cf0578598d186612e83409" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index a41540bdda..f44e888218 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -954,5 +954,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-a5591f76a7a8353e3d0dd83e83c16479678f73a9" + "buildInfoId": "solc-0_8_28-64228f31c3990e4616cf0578598d186612e83409" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index b218563f66..f6edab0a13 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -384,6 +384,12 @@ "name": "publicKey", "type": "bytes" }, + { + "indexed": false, + "internalType": "bytes32", + "name": "pkCommitment", + "type": "bytes32" + }, { "indexed": false, "internalType": "bytes", @@ -1026,13 +1032,13 @@ "type": "bytes" }, { - "internalType": "bytes", - "name": "proof", - "type": "bytes" + "internalType": "bytes32", + "name": "pkCommitment", + "type": "bytes32" }, { "internalType": "bytes", - "name": "foldProof", + "name": "proof", "type": "bytes" } ], @@ -1264,30 +1270,30 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b613ed1806100d65f395ff3fe608060405234801561000f575f5ffd5b506004361061023f575f3560e01c80639f0f874a11610135578063e59e4695116100b4578063f26ef74e11610079578063f26ef74e1461057f578063f2fde38b14610592578063f379b0df146105a5578063f52fd803146105df578063f6fc05d514610651575f5ffd5b8063e59e469514610524578063e6745e1314610537578063e82f3b701461054a578063ebf0c7171461055d578063f165053614610565575f5ffd5b8063c3a0ec30116100fa578063c3a0ec30146104bc578063ca2869a0146104cd578063cd6dc687146104ec578063da881e5a146104ff578063dbb06c9314610512575f5ffd5b80639f0f874a1461044e578063a016493014610457578063a8a4d69b14610477578063bff232c11461048a578063c2b40ae41461049d575f5ffd5b8063715018a6116101c15780638d1ddfb1116101865780638d1ddfb1146103ce5780638da5cb5b146103e45780638e5ce3ad146103ec5780639015d371146103ff5780639a7a2ffc14610412575f5ffd5b8063715018a6146103475780637c92f5241461034f578063858142431461037c5780638a78bb151461039c5780638cb89ecb146103af575f5ffd5b80632e7b716d116102075780632e7b716d146102d95780634d6861a6146102ec57806350e6d94c146102ff5780635d5047761461032157806370e36bbe14610334575f5ffd5b8063096b810a146102435780630f3e34121461025857806317d611201461026b5780632800d82914610295578063291a691b146102b6575b5f5ffd5b6102566102513660046132b5565b61065a565b005b6102566102663660046132d0565b6107a6565b61027e6102793660046132d0565b6107e9565b60405161028c92919061335a565b60405180910390f35b6102a86102a33660046132d0565b610993565b60405190815260200161028c565b6102c96102c4366004613387565b6109df565b604051901515815260200161028c565b6102c96102e73660046132b5565b610bb9565b6102c96102fa3660046132d0565b610c6c565b6102c961030d3660046132b5565b60066020525f908152604090205460ff1681565b6102c961032f3660046133c0565b610cab565b6102566103423660046132b5565b610cef565b610256610d65565b61036261035d3660046133ee565b610d78565b6040805192835263ffffffff90911660208301520161028c565b60015461038f906001600160a01b031681565b60405161028c9190613423565b6102566103aa3660046132b5565b610f1f565b6102a86103bd3660046132d0565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102a8565b61038f61105d565b600b5461038f906001600160a01b031681565b6102c961040d3660046132b5565b61108b565b6104386104203660046132b5565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161028c565b6102a860035481565b61046a6104653660046132d0565b6110a8565b60405161028c9190613437565b6102c96104853660046133c0565b61113e565b6102566104983660046132b5565b611182565b6102a86104ab3660046132d0565b60086020525f908152604090205481565b6001546001600160a01b031661038f565b6102a86104db3660046132d0565b5f9081526008602052604090205490565b6102566104fa366004613449565b6111d3565b6102c961050d3660046132d0565b611330565b5f5461038f906001600160a01b031681565b6102566105323660046132b5565b61160a565b610256610545366004613473565b611682565b6102a86105583660046132d0565b611845565b6102a8611876565b61056d601481565b60405160ff909116815260200161028c565b61025661058d3660046134d7565b611888565b6102566105a03660046132b5565b611b85565b6004546105c19064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161028c565b6106226105ed3660046132d0565b5f908152600a6020819052604090912090810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161028c949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102a860025481565b61066261105d565b6001600160a01b0316336001600160a01b0316148061068b57506001546001600160a01b031633145b6106a857604051632864c4e160e01b815260040160405180910390fd5b6106b18161108b565b81906106da576040516381e5828960e01b81526004016106d19190613423565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107089060049083611bbf565b6001600160a01b0382165f908152600660205260408120805460ff191690556002805491610735836135f1565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b6107ae611e61565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a60208190526040909120600681015491810154606092839291806001600160401b0381111561082057610820613606565b604051908082528060200260200182016040528015610849578160200160208202803683370190505b509450806001600160401b0381111561086457610864613606565b60405190808252806020026020018201604052801561088d578160200160208202803683370190505b5093505f805b83811015610989575f8560060182815481106108b1576108b161361a565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f90815260098801602052604090205460ff1660028111156108f7576108f761362e565b03610980578088848151811061090f5761090f61361a565b60200260200101906001600160a01b031690816001600160a01b031681525050856008015f826001600160a01b03166001600160a01b031681526020019081526020015f20548784815181106109675761096761361a565b60209081029190910101528261097c81613642565b9350505b50600101610893565b5050505050915091565b5f818152600a6020526040812081815460ff1660038111156109b7576109b761362e565b036109d557604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610a0a5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610a2e57610a2e61362e565b14610a4c576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610a93573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab7919061365a565b905080610aca6040860160208701613684565b63ffffffff161115610ae26040860160208701613684565b829091610b10576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106d1565b5050815460ff1916600190811783558201859055436002830155600354610b37904261369d565b6003830155610b4b600583018560026131ec565b50610b54611876565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ba5928a928a92916136b0565b60405180910390a250600195945050505050565b5f610bc38261108b565b610bce57505f919050565b6001546001600160a01b0316610bf7576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610c27908590600401613423565b602060405180830381865afa158015610c42573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c66919061370f565b92915050565b5f818152600a602052604081206001815460ff166003811115610c9157610c9161362e565b14610c9e57505f92915050565b6003015442111592915050565b5f60015f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff166002811115610ce757610ce761362e565b149392505050565b610cf7611e61565b6001600160a01b038116610d1e5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610d6d611e61565b610d765f611e93565b565b600b545f9081906001600160a01b03163314610da75760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff166003811115610dcc57610dcc61362e565b14610dea57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f90815260098301602052604090205463ffffffff909116925060019060ff166002811115610e2a57610e2a61362e565b14610e3a57600a01549150610f17565b6001600160a01b0385165f9081526009820160205260408120805460ff19166002179055600a8201805491610e6e836135f1565b919050555080600a01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610ebf929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b610f2761105d565b6001600160a01b0316336001600160a01b03161480610f5057506001546001600160a01b031633145b610f6d57604051632864c4e160e01b815260040160405180910390fd5b610f768161108b565b61105a5760048054600160281b900464ffffffffff1690610fa0906001600160a01b038416611f03565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff199091161790556002805491610ff183613642565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161079a565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a602052604090206004810154606091906110db576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561113157602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611113575b5050505050915050919050565b5f805f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff1660028111156111795761117961362e565b14159392505050565b61118a611e61565b6001600160a01b0381166111b15760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b5f6111dc6120d9565b805490915060ff600160401b82041615906001600160401b03165f811580156112025750825b90505f826001600160401b0316600114801561121d5750303b155b90508115801561122b575080155b156112495760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561127357845460ff60401b1916600160401b1785555b6001600160a01b03871661129a5760405163d92e233d60e01b815260040160405180910390fd5b6112a333612101565b6112af60046014612112565b6112b8866107a6565b6112c061105d565b6001600160a01b0316876001600160a01b0316146112e1576112e187611b85565b831561132757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff1660038111156113545761135461362e565b0361137257604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561138a5761138a61362e565b146113a857604051631860f69960e31b815260040160405180910390fd5b806003015442116113cc57604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff161115806114b1578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611492575f5ffd5b505af11580156114a4573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600a83018190555f816001600160401b038111156114e1576114e1613606565b60405190808252806020026020018201604052801561150a578160200160208202803683370190505b5090505f5b8281101561157c57846008015f8660060183815481106115315761153161361a565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106115695761156961361a565b602090810291909101015260010161150f565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b1580156115bf575f5ffd5b505af11580156115d1573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ba5929190613728565b611612611e61565b6001600160a01b0381166116395760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff1660038111156116a6576116a661362e565b036116c457604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116dc576116dc61362e565b146116fa57604051631860f69960e31b815260040160405180910390fd5b806003015442111561171f57604051639a19114d60e01b815260040160405180910390fd5b335f90815260078201602052604090205460ff16156117515760405163257309f160e11b815260040160405180910390fd5b61175a33610bb9565b6117775760405163149fbcfd60e11b815260040160405180910390fd5b611782338385612191565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526007850160205260409020805460ff1916600117905590915061180190839083612362565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611871576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61188360046014612563565b905090565b5f898152600a602052604090206002815460ff1660038111156118ad576118ad61362e565b146118cb57604051634f4b461f60e11b815260040160405180910390fd5b6004810154156118ee5760405163632a22bb60e01b815260040160405180910390fd5b600681015488146119375760405162461bcd60e51b815260206004820152601360248201527209cdec8ca40c6deeadce840dad2e6dac2e8c6d606b1b60448201526064016106d1565b5f6119448587018761387f565b9150505f81511161198e5760405162461bcd60e51b815260206004820152601460248201527343353a206e6f207075626c696320696e7075747360601b60448201526064016106d1565b5f816001835161199e9190613922565b815181106119ae576119ae61361a565b602002602001015190505f5f5f9054906101000a90046001600160a01b03166001600160a01b031663406ed35c8e6040518263ffffffff1660e01b81526004016119fa91815260200190565b5f60405180830381865afa158015611a14573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611a3b9190810190613a17565b9050806101c0015115611ac0578061012001516001600160a01b031663f7e83aee898989896040518563ffffffff1660e01b8152600401611a7f9493929190613b9d565b602060405180830381865afa158015611a9a573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611abe919061365a565b505b60048085018390555f8e815260096020526040808220859055905490516340a3b76160e11b81529182018f9052602482018490526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015611b1e575f5ffd5b505af1158015611b30573d5f5f3e3d5ffd5b505050508c7f49ac1dd411942113d1c5e6799c6379ce341afe85a4175fb562cf2a5fb886c27d8d8d8d8d8d8d604051611b6e96959493929190613bce565b60405180910390a250505050505050505050505050565b611b8d611e61565b6001600160a01b038116611bb6575f604051631e4fbdf760e01b81526004016106d19190613423565b61105a81611e93565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611bfe5760405162461bcd60e51b81526004016106d190613c47565b825464ffffffffff600160281b90910481169082168111611c5c5760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106d1565b825f5b81866001015f611c6f848861265c565b64ffffffffff1681526020019081526020015f20819055505f816001611c959190613c91565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611cca5750611e59565b600185165f03611d91575f611ce983611ce4886001613caa565b61265c565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611d4a91600401613cc7565b602060405180830381865af4158015611d65573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d89919061365a565b935050611e45565b5f611da183611ce4600189613cf7565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e0291600401613cc7565b602060405180830381865af4158015611e1d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e41919061365a565b9350505b50647fffffffff600194851c169301611c5f565b505050505050565b33611e6a61105d565b6001600160a01b031614610d76573360405163118cdaa760e01b81526004016106d19190613423565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611f525760405162461bcd60e51b81526004016106d190613c47565b825464ffffffffff90811690821610611fa55760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106d1565b611fb0816001613caa565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f611fe7848761265c565b64ffffffffff16815260208101919091526040015f205560018316156120d2575f61201782611ce4600187613cf7565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161207891600401613cc7565b602060405180830381865af4158015612093573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120b7919061365a565b647fffffffff600195861c1694909350919091019050611fd7565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610c66565b612109612679565b61105a8161269e565b602060ff821611156121605760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106d1565b612171600160ff831681901b613922565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116121b15760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166121da576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161221091613922565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612257573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061227b919061365a565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122ce573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122f2919061365a565b90505f81116123145760405163aeaddff160e01b815260040160405180910390fd5b5f61231f8284613d14565b90505f81116123415760405163149fbcfd60e11b815260040160405180910390fd5b808611156113275760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff16908111156123e057508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526008870182526040808420869055600988019092529120805460ff191682179055905061255c565b5f5f90505f876008015f855f815481106123fc576123fc61361a565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612484575f896008015f8784815481106124465761244661361a565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205490508281111561247b578092508193505b50600101612425565b50808610612498575f94505050505061255c565b5f886009015f8685815481106124b0576124b061361a565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156124ed576124ed61362e565b0217905550868483815481106125055761250561361a565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260088a018252604080822089905560098b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff16116125b65760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106d1565b602060ff831611156125da5760405162461bcd60e51b81526004016106d190613d33565b8254600160281b900464ffffffffff16806125f960ff85166002613e84565b64ffffffffff1610156126495760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106d1565b6126548482856126a6565b949350505050565b5f8161266f60ff851663ffffffff613e9d565b61255c9190613caa565b61268161276e565b610d7657604051631afcd79f60e31b815260040160405180910390fd5b611b8d612679565b5f602060ff831611156126cb5760405162461bcd60e51b81526004016106d190613d33565b8264ffffffffff165f036126e9576126e282612787565b905061255c565b5f6126f5836001613c91565b60ff166001600160401b0381111561270f5761270f613606565b604051908082528060200260200182016040528015612738578160200160208202803683370190505b50905061274785858584612e21565b808360ff168151811061275c5761275c61361a565b60200260200101519150509392505050565b5f6127776120d9565b54600160401b900460ff16919050565b5f8160ff165f0361279957505f919050565b8160ff166001036127cb57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036127fd57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361282f57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361286157507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361289357507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036128c557507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036128f757507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361292957507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361295b57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361298d57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036129bf57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c036129f157507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612a2357507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612a5557507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612a8757507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612ab957507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612aeb57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612b1d57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612b4f57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612b8157507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612bb357507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612be557507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612c1757507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612c4957507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612c7b57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612cad57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612cdf57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612d1157507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612d4357507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612d7557507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612da757507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612dd957507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106d1565b602060ff83161115612e455760405162461bcd60e51b81526004016106d190613d33565b5f8364ffffffffff1611612ea95760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106d1565b5f612eb5600185613cf7565b9050600181165f03612f0857846001015f612ed05f8461265c565b64ffffffffff1681526020019081526020015f2054825f81518110612ef757612ef761361a565b602002602001018181525050612f30565b612f115f612787565b825f81518110612f2357612f2361361a565b6020026020010181815250505b5f5b8360ff168160ff161015611e5957600182165f036130285773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612f8457612f8461361a565b60200260200101518152602001612f9a85612787565b8152506040518263ffffffff1660e01b8152600401612fb99190613cc7565b602060405180830381865af4158015612fd4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ff8919061365a565b83613004836001613c91565b60ff16815181106130175761301761361a565b6020026020010181815250506131d9565b5f613034826001613c91565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156130d6575f876001015f61308b85600161307a9190613c91565b60018864ffffffffff16901c61265c565b64ffffffffff1681526020019081526020015f2054905080858460016130b19190613c91565b60ff16815181106130c4576130c461361a565b602002602001018181525050506131d7565b5f876001015f6130ed85600188611ce49190613cf7565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106131445761314461361a565b60200260200101518152506040518263ffffffff1660e01b815260040161316b9190613cc7565b602060405180830381865af4158015613186573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131aa919061365a565b856131b6856001613c91565b60ff16815181106131c9576131c961361a565b602002602001018181525050505b505b647fffffffff600192831c169101612f32565b60018301918390821561327d579160200282015f5b8382111561324b57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613201565b801561327b5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261324b565b505b5061328992915061328d565b5090565b5b80821115613289575f815560010161328e565b6001600160a01b038116811461105a575f5ffd5b5f602082840312156132c5575f5ffd5b813561255c816132a1565b5f602082840312156132e0575f5ffd5b5035919050565b5f8151808452602084019350602083015f5b828110156133205781516001600160a01b03168652602095860195909101906001016132f9565b5093949350505050565b5f8151808452602084019350602083015f5b8281101561332057815186526020958601959091019060010161333c565b604081525f61336c60408301856132e7565b828103602084015261337e818561332a565b95945050505050565b5f5f5f60808486031215613399575f5ffd5b8335925060208401359150608084018510156133b3575f5ffd5b6040840190509250925092565b5f5f604083850312156133d1575f5ffd5b8235915060208301356133e3816132a1565b809150509250929050565b5f5f5f60608486031215613400575f5ffd5b833592506020840135613412816132a1565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f61255c60208301846132e7565b5f5f6040838503121561345a575f5ffd5b8235613465816132a1565b946020939093013593505050565b5f5f60408385031215613484575f5ffd5b50508035926020909101359150565b5f5f83601f8401126134a3575f5ffd5b5081356001600160401b038111156134b9575f5ffd5b6020830191508360208285010111156134d0575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f5f60a08a8c0312156134ef575f5ffd5b8935985060208a01356001600160401b0381111561350b575f5ffd5b8a01601f81018c1361351b575f5ffd5b80356001600160401b03811115613530575f5ffd5b8c60208260051b8401011115613544575f5ffd5b6020919091019850965060408a01356001600160401b03811115613566575f5ffd5b6135728c828d01613493565b90975095505060608a01356001600160401b03811115613590575f5ffd5b61359c8c828d01613493565b90955093505060808a01356001600160401b038111156135ba575f5ffd5b6135c68c828d01613493565b915080935050809150509295985092959850929598565b634e487b7160e01b5f52601160045260245ffd5b5f816135ff576135ff6135dd565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f60018201613653576136536135dd565b5060010190565b5f6020828403121561366a575f5ffd5b5051919050565b803563ffffffff81168114611871575f5ffd5b5f60208284031215613694575f5ffd5b61255c82613671565b80820180821115610c6657610c666135dd565b84815260a0810160208201855f5b60028110156136eb5763ffffffff6136d583613671565b16835260209283019291909101906001016136be565b50505060608201939093526080015292915050565b80518015158114611871575f5ffd5b5f6020828403121561371f575f5ffd5b61255c82613700565b604080825283549082018190525f8481526020812090916060840190835b8181101561376d5783546001600160a01b0316835260019384019360209093019201613746565b50508381036020850152613781818661332a565b9695505050505050565b6040516101e081016001600160401b03811182821017156137ae576137ae613606565b60405290565b604051601f8201601f191681016001600160401b03811182821017156137dc576137dc613606565b604052919050565b5f6001600160401b038211156137fc576137fc613606565b50601f01601f191660200190565b5f82601f830112613819575f5ffd5b81356001600160401b0381111561383257613832613606565b8060051b613842602082016137b4565b9182526020818501810192908101908684111561385d575f5ffd5b6020860192505b83831015613781578235825260209283019290910190613864565b5f5f60408385031215613890575f5ffd5b82356001600160401b038111156138a5575f5ffd5b8301601f810185136138b5575f5ffd5b80356138c86138c3826137e4565b6137b4565b8181528660208385010111156138dc575f5ffd5b816020840160208301375f6020838301015280945050505060208301356001600160401b0381111561390c575f5ffd5b6139188582860161380a565b9150509250929050565b81810381811115610c6657610c666135dd565b805160048110611871575f5ffd5b5f82601f830112613952575f5ffd5b604080519081016001600160401b038111828210171561397457613974613606565b806040525080604084018581111561398a575f5ffd5b845b818110156139a457805183526020928301920161398c565b509195945050505050565b8051611871816132a1565b805160ff81168114611871575f5ffd5b5f82601f8301126139d9575f5ffd5b81516139e76138c3826137e4565b8181528460208386010111156139fb575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f60208284031215613a27575f5ffd5b81516001600160401b03811115613a3c575f5ffd5b82016102008185031215613a4e575f5ffd5b613a5661378b565b81518152613a6660208301613935565b602082015260408281015190820152613a828560608401613943565b606082015260a08201516080820152613a9d60c083016139af565b60a0820152613aae60e083016139ba565b60c08201526101008201516001600160401b03811115613acc575f5ffd5b613ad8868285016139ca565b60e083015250613aeb61012083016139af565b610100820152613afe61014083016139af565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613b34575f5ffd5b613b40868285016139ca565b61018083015250613b546101c083016139af565b6101a0820152613b676101e08301613700565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f613bb0604083018688613b75565b8281036020840152613bc3818587613b75565b979650505050505050565b606080825281018690525f8760808301825b89811015613c10578235613bf3816132a1565b6001600160a01b0316825260209283019290910190600101613be0565b508381036020850152613c2481888a613b75565b9150508281036040840152613c3a818587613b75565b9998505050505050505050565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610c6657610c666135dd565b64ffffffffff8181168382160190811115610c6657610c666135dd565b6040810181835f5b6002811015613cee578151835260209283019290910190600101613ccf565b50505092915050565b64ffffffffff8281168282160390811115610c6657610c666135dd565b5f82613d2e57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115610f1757808504811115613d9557613d956135dd565b6001841615613da357908102905b60019390931c928002613d7a565b5f82613dbf57506001610c66565b81613dcb57505f610c66565b8160018114613de15760028114613deb57613e1d565b6001915050610c66565b60ff841115613dfc57613dfc6135dd565b6001841b915064ffffffffff821115613e1757613e176135dd565b50610c66565b5060208310610133831016604e8410600b8410161715613e55575081810a64ffffffffff811115613e5057613e506135dd565b610c66565b613e6564ffffffffff8484613d76565b8064ffffffffff04821115613e7c57613e7c6135dd565b029392505050565b5f61255c64ffffffffff841664ffffffffff8416613db1565b64ffffffffff8181168382160290811690818114613ebd57613ebd6135dd565b509291505056fea164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b506004361061023f575f3560e01c80639f0f874a11610135578063e59e4695116100b4578063f26ef74e11610079578063f26ef74e1461057f578063f2fde38b14610592578063f379b0df146105a5578063f52fd803146105df578063f6fc05d514610651575f5ffd5b8063e59e469514610524578063e6745e1314610537578063e82f3b701461054a578063ebf0c7171461055d578063f165053614610565575f5ffd5b8063c3a0ec30116100fa578063c3a0ec30146104bc578063ca2869a0146104cd578063cd6dc687146104ec578063da881e5a146104ff578063dbb06c9314610512575f5ffd5b80639f0f874a1461044e578063a016493014610457578063a8a4d69b14610477578063bff232c11461048a578063c2b40ae41461049d575f5ffd5b8063715018a6116101c15780638d1ddfb1116101865780638d1ddfb1146103ce5780638da5cb5b146103e45780638e5ce3ad146103ec5780639015d371146103ff5780639a7a2ffc14610412575f5ffd5b8063715018a6146103475780637c92f5241461034f578063858142431461037c5780638a78bb151461039c5780638cb89ecb146103af575f5ffd5b80632e7b716d116102075780632e7b716d146102d95780634d6861a6146102ec57806350e6d94c146102ff5780635d5047761461032157806370e36bbe14610334575f5ffd5b8063096b810a146102435780630f3e34121461025857806317d611201461026b5780632800d82914610295578063291a691b146102b6575b5f5ffd5b6102566102513660046132b5565b61065a565b005b6102566102663660046132d0565b6107a6565b61027e6102793660046132d0565b6107e9565b60405161028c92919061335a565b60405180910390f35b6102a86102a33660046132d0565b610993565b60405190815260200161028c565b6102c96102c4366004613387565b6109df565b604051901515815260200161028c565b6102c96102e73660046132b5565b610bb9565b6102c96102fa3660046132d0565b610c6c565b6102c961030d3660046132b5565b60066020525f908152604090205460ff1681565b6102c961032f3660046133c0565b610cab565b6102566103423660046132b5565b610cef565b610256610d65565b61036261035d3660046133ee565b610d78565b6040805192835263ffffffff90911660208301520161028c565b60015461038f906001600160a01b031681565b60405161028c9190613423565b6102566103aa3660046132b5565b610f1f565b6102a86103bd3660046132d0565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102a8565b61038f61105d565b600b5461038f906001600160a01b031681565b6102c961040d3660046132b5565b61108b565b6104386104203660046132b5565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161028c565b6102a860035481565b61046a6104653660046132d0565b6110a8565b60405161028c9190613437565b6102c96104853660046133c0565b61113e565b6102566104983660046132b5565b611182565b6102a86104ab3660046132d0565b60086020525f908152604090205481565b6001546001600160a01b031661038f565b6102a86104db3660046132d0565b5f9081526008602052604090205490565b6102566104fa366004613449565b6111d3565b6102c961050d3660046132d0565b611330565b5f5461038f906001600160a01b031681565b6102566105323660046132b5565b61160a565b610256610545366004613473565b611682565b6102a86105583660046132d0565b611845565b6102a8611876565b61056d601481565b60405160ff909116815260200161028c565b61025661058d3660046134d7565b611888565b6102566105a03660046132b5565b611b85565b6004546105c19064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161028c565b6106226105ed3660046132d0565b5f908152600a6020819052604090912090810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161028c949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102a860025481565b61066261105d565b6001600160a01b0316336001600160a01b0316148061068b57506001546001600160a01b031633145b6106a857604051632864c4e160e01b815260040160405180910390fd5b6106b18161108b565b81906106da576040516381e5828960e01b81526004016106d19190613423565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107089060049083611bbf565b6001600160a01b0382165f908152600660205260408120805460ff191690556002805491610735836135f1565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b6107ae611e61565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a60208190526040909120600681015491810154606092839291806001600160401b0381111561082057610820613606565b604051908082528060200260200182016040528015610849578160200160208202803683370190505b509450806001600160401b0381111561086457610864613606565b60405190808252806020026020018201604052801561088d578160200160208202803683370190505b5093505f805b83811015610989575f8560060182815481106108b1576108b161361a565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f90815260098801602052604090205460ff1660028111156108f7576108f761362e565b03610980578088848151811061090f5761090f61361a565b60200260200101906001600160a01b031690816001600160a01b031681525050856008015f826001600160a01b03166001600160a01b031681526020019081526020015f20548784815181106109675761096761361a565b60209081029190910101528261097c81613642565b9350505b50600101610893565b5050505050915091565b5f818152600a6020526040812081815460ff1660038111156109b7576109b761362e565b036109d557604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610a0a5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610a2e57610a2e61362e565b14610a4c576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610a93573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab7919061365a565b905080610aca6040860160208701613684565b63ffffffff161115610ae26040860160208701613684565b829091610b10576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106d1565b5050815460ff1916600190811783558201859055436002830155600354610b37904261369d565b6003830155610b4b600583018560026131ec565b50610b54611876565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ba5928a928a92916136b0565b60405180910390a250600195945050505050565b5f610bc38261108b565b610bce57505f919050565b6001546001600160a01b0316610bf7576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610c27908590600401613423565b602060405180830381865afa158015610c42573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c66919061370f565b92915050565b5f818152600a602052604081206001815460ff166003811115610c9157610c9161362e565b14610c9e57505f92915050565b6003015442111592915050565b5f60015f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff166002811115610ce757610ce761362e565b149392505050565b610cf7611e61565b6001600160a01b038116610d1e5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610d6d611e61565b610d765f611e93565b565b600b545f9081906001600160a01b03163314610da75760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff166003811115610dcc57610dcc61362e565b14610dea57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f90815260098301602052604090205463ffffffff909116925060019060ff166002811115610e2a57610e2a61362e565b14610e3a57600a01549150610f17565b6001600160a01b0385165f9081526009820160205260408120805460ff19166002179055600a8201805491610e6e836135f1565b919050555080600a01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610ebf929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b610f2761105d565b6001600160a01b0316336001600160a01b03161480610f5057506001546001600160a01b031633145b610f6d57604051632864c4e160e01b815260040160405180910390fd5b610f768161108b565b61105a5760048054600160281b900464ffffffffff1690610fa0906001600160a01b038416611f03565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff199091161790556002805491610ff183613642565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161079a565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a602052604090206004810154606091906110db576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561113157602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611113575b5050505050915050919050565b5f805f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff1660028111156111795761117961362e565b14159392505050565b61118a611e61565b6001600160a01b0381166111b15760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b5f6111dc6120d9565b805490915060ff600160401b82041615906001600160401b03165f811580156112025750825b90505f826001600160401b0316600114801561121d5750303b155b90508115801561122b575080155b156112495760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561127357845460ff60401b1916600160401b1785555b6001600160a01b03871661129a5760405163d92e233d60e01b815260040160405180910390fd5b6112a333612101565b6112af60046014612112565b6112b8866107a6565b6112c061105d565b6001600160a01b0316876001600160a01b0316146112e1576112e187611b85565b831561132757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff1660038111156113545761135461362e565b0361137257604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561138a5761138a61362e565b146113a857604051631860f69960e31b815260040160405180910390fd5b806003015442116113cc57604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff161115806114b1578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611492575f5ffd5b505af11580156114a4573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600a83018190555f816001600160401b038111156114e1576114e1613606565b60405190808252806020026020018201604052801561150a578160200160208202803683370190505b5090505f5b8281101561157c57846008015f8660060183815481106115315761153161361a565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106115695761156961361a565b602090810291909101015260010161150f565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b1580156115bf575f5ffd5b505af11580156115d1573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ba5929190613728565b611612611e61565b6001600160a01b0381166116395760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff1660038111156116a6576116a661362e565b036116c457604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116dc576116dc61362e565b146116fa57604051631860f69960e31b815260040160405180910390fd5b806003015442111561171f57604051639a19114d60e01b815260040160405180910390fd5b335f90815260078201602052604090205460ff16156117515760405163257309f160e11b815260040160405180910390fd5b61175a33610bb9565b6117775760405163149fbcfd60e11b815260040160405180910390fd5b611782338385612191565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526007850160205260409020805460ff1916600117905590915061180190839083612362565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611871576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61188360046014612563565b905090565b5f898152600a602052604090206002815460ff1660038111156118ad576118ad61362e565b146118cb57604051634f4b461f60e11b815260040160405180910390fd5b6004810154156118ee5760405163632a22bb60e01b815260040160405180910390fd5b600681015488146119375760405162461bcd60e51b815260206004820152601360248201527209cdec8ca40c6deeadce840dad2e6dac2e8c6d606b1b60448201526064016106d1565b5f6119448587018761387f565b9150505f81511161198e5760405162461bcd60e51b815260206004820152601460248201527343353a206e6f207075626c696320696e7075747360601b60448201526064016106d1565b5f816001835161199e9190613922565b815181106119ae576119ae61361a565b602002602001015190505f5f5f9054906101000a90046001600160a01b03166001600160a01b031663406ed35c8e6040518263ffffffff1660e01b81526004016119fa91815260200190565b5f60405180830381865afa158015611a14573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611a3b9190810190613a17565b9050806101c0015115611ac0578061012001516001600160a01b031663f7e83aee898989896040518563ffffffff1660e01b8152600401611a7f9493929190613b9d565b602060405180830381865afa158015611a9a573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611abe919061365a565b505b60048085018390555f8e815260096020526040808220859055905490516340a3b76160e11b81529182018f9052602482018490526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015611b1e575f5ffd5b505af1158015611b30573d5f5f3e3d5ffd5b505050508c7f49ac1dd411942113d1c5e6799c6379ce341afe85a4175fb562cf2a5fb886c27d8d8d8d8d8d8d604051611b6e96959493929190613bce565b60405180910390a250505050505050505050505050565b611b8d611e61565b6001600160a01b038116611bb6575f604051631e4fbdf760e01b81526004016106d19190613423565b61105a81611e93565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611bfe5760405162461bcd60e51b81526004016106d190613c47565b825464ffffffffff600160281b90910481169082168111611c5c5760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106d1565b825f5b81866001015f611c6f848861265c565b64ffffffffff1681526020019081526020015f20819055505f816001611c959190613c91565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611cca5750611e59565b600185165f03611d91575f611ce983611ce4886001613caa565b61265c565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611d4a91600401613cc7565b602060405180830381865af4158015611d65573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d89919061365a565b935050611e45565b5f611da183611ce4600189613cf7565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e0291600401613cc7565b602060405180830381865af4158015611e1d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e41919061365a565b9350505b50647fffffffff600194851c169301611c5f565b505050505050565b33611e6a61105d565b6001600160a01b031614610d76573360405163118cdaa760e01b81526004016106d19190613423565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611f525760405162461bcd60e51b81526004016106d190613c47565b825464ffffffffff90811690821610611fa55760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106d1565b611fb0816001613caa565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f611fe7848761265c565b64ffffffffff16815260208101919091526040015f205560018316156120d2575f61201782611ce4600187613cf7565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161207891600401613cc7565b602060405180830381865af4158015612093573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120b7919061365a565b647fffffffff600195861c1694909350919091019050611fd7565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610c66565b612109612679565b61105a8161269e565b602060ff821611156121605760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106d1565b612171600160ff831681901b613922565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116121b15760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166121da576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161221091613922565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612257573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061227b919061365a565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122ce573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122f2919061365a565b90505f81116123145760405163aeaddff160e01b815260040160405180910390fd5b5f61231f8284613d14565b90505f81116123415760405163149fbcfd60e11b815260040160405180910390fd5b808611156113275760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff16908111156123e057508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526008870182526040808420869055600988019092529120805460ff191682179055905061255c565b5f5f90505f876008015f855f815481106123fc576123fc61361a565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612484575f896008015f8784815481106124465761244661361a565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205490508281111561247b578092508193505b50600101612425565b50808610612498575f94505050505061255c565b5f886009015f8685815481106124b0576124b061361a565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156124ed576124ed61362e565b0217905550868483815481106125055761250561361a565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260088a018252604080822089905560098b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff16116125b65760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106d1565b602060ff831611156125da5760405162461bcd60e51b81526004016106d190613d33565b8254600160281b900464ffffffffff16806125f960ff85166002613e84565b64ffffffffff1610156126495760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106d1565b6126548482856126a6565b949350505050565b5f8161266f60ff851663ffffffff613e9d565b61255c9190613caa565b61268161276e565b610d7657604051631afcd79f60e31b815260040160405180910390fd5b611b8d612679565b5f602060ff831611156126cb5760405162461bcd60e51b81526004016106d190613d33565b8264ffffffffff165f036126e9576126e282612787565b905061255c565b5f6126f5836001613c91565b60ff166001600160401b0381111561270f5761270f613606565b604051908082528060200260200182016040528015612738578160200160208202803683370190505b50905061274785858584612e21565b808360ff168151811061275c5761275c61361a565b60200260200101519150509392505050565b5f6127776120d9565b54600160401b900460ff16919050565b5f8160ff165f0361279957505f919050565b8160ff166001036127cb57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036127fd57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361282f57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361286157507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361289357507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036128c557507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036128f757507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361292957507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361295b57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361298d57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036129bf57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c036129f157507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612a2357507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612a5557507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612a8757507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612ab957507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612aeb57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612b1d57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612b4f57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612b8157507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612bb357507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612be557507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612c1757507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612c4957507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612c7b57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612cad57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612cdf57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612d1157507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612d4357507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612d7557507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612da757507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612dd957507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106d1565b602060ff83161115612e455760405162461bcd60e51b81526004016106d190613d33565b5f8364ffffffffff1611612ea95760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106d1565b5f612eb5600185613cf7565b9050600181165f03612f0857846001015f612ed05f8461265c565b64ffffffffff1681526020019081526020015f2054825f81518110612ef757612ef761361a565b602002602001018181525050612f30565b612f115f612787565b825f81518110612f2357612f2361361a565b6020026020010181815250505b5f5b8360ff168160ff161015611e5957600182165f036130285773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612f8457612f8461361a565b60200260200101518152602001612f9a85612787565b8152506040518263ffffffff1660e01b8152600401612fb99190613cc7565b602060405180830381865af4158015612fd4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ff8919061365a565b83613004836001613c91565b60ff16815181106130175761301761361a565b6020026020010181815250506131d9565b5f613034826001613c91565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156130d6575f876001015f61308b85600161307a9190613c91565b60018864ffffffffff16901c61265c565b64ffffffffff1681526020019081526020015f2054905080858460016130b19190613c91565b60ff16815181106130c4576130c461361a565b602002602001018181525050506131d7565b5f876001015f6130ed85600188611ce49190613cf7565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106131445761314461361a565b60200260200101518152506040518263ffffffff1660e01b815260040161316b9190613cc7565b602060405180830381865af4158015613186573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131aa919061365a565b856131b6856001613c91565b60ff16815181106131c9576131c961361a565b602002602001018181525050505b505b647fffffffff600192831c169101612f32565b60018301918390821561327d579160200282015f5b8382111561324b57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613201565b801561327b5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261324b565b505b5061328992915061328d565b5090565b5b80821115613289575f815560010161328e565b6001600160a01b038116811461105a575f5ffd5b5f602082840312156132c5575f5ffd5b813561255c816132a1565b5f602082840312156132e0575f5ffd5b5035919050565b5f8151808452602084019350602083015f5b828110156133205781516001600160a01b03168652602095860195909101906001016132f9565b5093949350505050565b5f8151808452602084019350602083015f5b8281101561332057815186526020958601959091019060010161333c565b604081525f61336c60408301856132e7565b828103602084015261337e818561332a565b95945050505050565b5f5f5f60808486031215613399575f5ffd5b8335925060208401359150608084018510156133b3575f5ffd5b6040840190509250925092565b5f5f604083850312156133d1575f5ffd5b8235915060208301356133e3816132a1565b809150509250929050565b5f5f5f60608486031215613400575f5ffd5b833592506020840135613412816132a1565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f61255c60208301846132e7565b5f5f6040838503121561345a575f5ffd5b8235613465816132a1565b946020939093013593505050565b5f5f60408385031215613484575f5ffd5b50508035926020909101359150565b5f5f83601f8401126134a3575f5ffd5b5081356001600160401b038111156134b9575f5ffd5b6020830191508360208285010111156134d0575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f5f60a08a8c0312156134ef575f5ffd5b8935985060208a01356001600160401b0381111561350b575f5ffd5b8a01601f81018c1361351b575f5ffd5b80356001600160401b03811115613530575f5ffd5b8c60208260051b8401011115613544575f5ffd5b6020919091019850965060408a01356001600160401b03811115613566575f5ffd5b6135728c828d01613493565b90975095505060608a01356001600160401b03811115613590575f5ffd5b61359c8c828d01613493565b90955093505060808a01356001600160401b038111156135ba575f5ffd5b6135c68c828d01613493565b915080935050809150509295985092959850929598565b634e487b7160e01b5f52601160045260245ffd5b5f816135ff576135ff6135dd565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f60018201613653576136536135dd565b5060010190565b5f6020828403121561366a575f5ffd5b5051919050565b803563ffffffff81168114611871575f5ffd5b5f60208284031215613694575f5ffd5b61255c82613671565b80820180821115610c6657610c666135dd565b84815260a0810160208201855f5b60028110156136eb5763ffffffff6136d583613671565b16835260209283019291909101906001016136be565b50505060608201939093526080015292915050565b80518015158114611871575f5ffd5b5f6020828403121561371f575f5ffd5b61255c82613700565b604080825283549082018190525f8481526020812090916060840190835b8181101561376d5783546001600160a01b0316835260019384019360209093019201613746565b50508381036020850152613781818661332a565b9695505050505050565b6040516101e081016001600160401b03811182821017156137ae576137ae613606565b60405290565b604051601f8201601f191681016001600160401b03811182821017156137dc576137dc613606565b604052919050565b5f6001600160401b038211156137fc576137fc613606565b50601f01601f191660200190565b5f82601f830112613819575f5ffd5b81356001600160401b0381111561383257613832613606565b8060051b613842602082016137b4565b9182526020818501810192908101908684111561385d575f5ffd5b6020860192505b83831015613781578235825260209283019290910190613864565b5f5f60408385031215613890575f5ffd5b82356001600160401b038111156138a5575f5ffd5b8301601f810185136138b5575f5ffd5b80356138c86138c3826137e4565b6137b4565b8181528660208385010111156138dc575f5ffd5b816020840160208301375f6020838301015280945050505060208301356001600160401b0381111561390c575f5ffd5b6139188582860161380a565b9150509250929050565b81810381811115610c6657610c666135dd565b805160048110611871575f5ffd5b5f82601f830112613952575f5ffd5b604080519081016001600160401b038111828210171561397457613974613606565b806040525080604084018581111561398a575f5ffd5b845b818110156139a457805183526020928301920161398c565b509195945050505050565b8051611871816132a1565b805160ff81168114611871575f5ffd5b5f82601f8301126139d9575f5ffd5b81516139e76138c3826137e4565b8181528460208386010111156139fb575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f60208284031215613a27575f5ffd5b81516001600160401b03811115613a3c575f5ffd5b82016102008185031215613a4e575f5ffd5b613a5661378b565b81518152613a6660208301613935565b602082015260408281015190820152613a828560608401613943565b606082015260a08201516080820152613a9d60c083016139af565b60a0820152613aae60e083016139ba565b60c08201526101008201516001600160401b03811115613acc575f5ffd5b613ad8868285016139ca565b60e083015250613aeb61012083016139af565b610100820152613afe61014083016139af565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613b34575f5ffd5b613b40868285016139ca565b61018083015250613b546101c083016139af565b6101a0820152613b676101e08301613700565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f613bb0604083018688613b75565b8281036020840152613bc3818587613b75565b979650505050505050565b606080825281018690525f8760808301825b89811015613c10578235613bf3816132a1565b6001600160a01b0316825260209283019290910190600101613be0565b508381036020850152613c2481888a613b75565b9150508281036040840152613c3a818587613b75565b9998505050505050505050565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610c6657610c666135dd565b64ffffffffff8181168382160190811115610c6657610c666135dd565b6040810181835f5b6002811015613cee578151835260209283019290910190600101613ccf565b50505092915050565b64ffffffffff8281168282160390811115610c6657610c666135dd565b5f82613d2e57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115610f1757808504811115613d9557613d956135dd565b6001841615613da357908102905b60019390931c928002613d7a565b5f82613dbf57506001610c66565b81613dcb57505f610c66565b8160018114613de15760028114613deb57613e1d565b6001915050610c66565b60ff841115613dfc57613dfc6135dd565b6001841b915064ffffffffff821115613e1757613e176135dd565b50610c66565b5060208310610133831016604e8410600b8410161715613e55575081810a64ffffffffff811115613e5057613e506135dd565b610c66565b613e6564ffffffffff8484613d76565b8064ffffffffff04821115613e7c57613e7c6135dd565b029392505050565b5f61255c64ffffffffff841664ffffffffff8416613db1565b64ffffffffff8181168382160290811690818114613ebd57613ebd6135dd565b509291505056fea164736f6c634300081c000a", + "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b613d8e806100d65f395ff3fe608060405234801561000f575f5ffd5b506004361061023f575f3560e01c80639a7a2ffc11610135578063dbb06c93116100b4578063f165053611610079578063f165053614610578578063f2fde38b14610592578063f379b0df146105a5578063f52fd803146105df578063f6fc05d514610651575f5ffd5b8063dbb06c9314610525578063e59e469514610537578063e6745e131461054a578063e82f3b701461055d578063ebf0c71714610570575f5ffd5b8063c2b40ae4116100fa578063c2b40ae4146104b0578063c3a0ec30146104cf578063ca2869a0146104e0578063cd6dc687146104ff578063da881e5a14610512575f5ffd5b80639a7a2ffc146104255780639f0f874a14610461578063a01649301461046a578063a8a4d69b1461048a578063bff232c11461049d575f5ffd5b806370e36bbe116101c15780638cb89ecb116101865780638cb89ecb146103c25780638d1ddfb1146103e15780638da5cb5b146103f75780638e5ce3ad146103ff5780639015d37114610412575f5ffd5b806370e36bbe14610347578063715018a61461035a5780637c92f52414610362578063858142431461038f5780638a78bb15146103af575f5ffd5b8063291a691b11610207578063291a691b146102c95780632e7b716d146102ec5780634d6861a6146102ff57806350e6d94c146103125780635d50477614610334575f5ffd5b8063096b810a146102435780630f3e34121461025857806317d611201461026b5780631cc321b5146102955780632800d829146102a8575b5f5ffd5b6102566102513660046132dc565b61065a565b005b6102566102663660046132f7565b6107a6565b61027e6102793660046132f7565b6107e9565b60405161028c929190613381565b60405180910390f35b6102566102a33660046133f2565b610993565b6102bb6102b63660046132f7565b610cb7565b60405190815260200161028c565b6102dc6102d73660046134d1565b610d03565b604051901515815260200161028c565b6102dc6102fa3660046132dc565b610edd565b6102dc61030d3660046132f7565b610f90565b6102dc6103203660046132dc565b60066020525f908152604090205460ff1681565b6102dc61034236600461350a565b610fcf565b6102566103553660046132dc565b611013565b610256611089565b610375610370366004613538565b61109c565b6040805192835263ffffffff90911660208301520161028c565b6001546103a2906001600160a01b031681565b60405161028c919061356d565b6102566103bd3660046132dc565b611243565b6102bb6103d03660046132f7565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102bb565b6103a2611381565b600b546103a2906001600160a01b031681565b6102dc6104203660046132dc565b6113af565b61044b6104333660046132dc565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161028c565b6102bb60035481565b61047d6104783660046132f7565b6113cc565b60405161028c9190613581565b6102dc61049836600461350a565b611462565b6102566104ab3660046132dc565b6114a6565b6102bb6104be3660046132f7565b60086020525f908152604090205481565b6001546001600160a01b03166103a2565b6102bb6104ee3660046132f7565b5f9081526008602052604090205490565b61025661050d366004613593565b6114f7565b6102dc6105203660046132f7565b611654565b5f546103a2906001600160a01b031681565b6102566105453660046132dc565b61192e565b6102566105583660046135bd565b6119a6565b6102bb61056b3660046132f7565b611b69565b6102bb611b9a565b610580601481565b60405160ff909116815260200161028c565b6102566105a03660046132dc565b611bac565b6004546105c19064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161028c565b6106226105ed3660046132f7565b5f908152600a6020819052604090912090810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161028c949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102bb60025481565b610662611381565b6001600160a01b0316336001600160a01b0316148061068b57506001546001600160a01b031633145b6106a857604051632864c4e160e01b815260040160405180910390fd5b6106b1816113af565b81906106da576040516381e5828960e01b81526004016106d1919061356d565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107089060049083611be6565b6001600160a01b0382165f908152600660205260408120805460ff191690556002805491610735836135f1565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b6107ae611e88565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a60208190526040909120600681015491810154606092839291806001600160401b0381111561082057610820613606565b604051908082528060200260200182016040528015610849578160200160208202803683370190505b509450806001600160401b0381111561086457610864613606565b60405190808252806020026020018201604052801561088d578160200160208202803683370190505b5093505f805b83811015610989575f8560060182815481106108b1576108b161361a565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f90815260098801602052604090205460ff1660028111156108f7576108f761362e565b03610980578088848151811061090f5761090f61361a565b60200260200101906001600160a01b031690816001600160a01b031681525050856008015f826001600160a01b03166001600160a01b031681526020019081526020015f20548784815181106109675761096761361a565b60209081029190910101528261097c81613642565b9350505b50600101610893565b5050505050915091565b5f888152600a602052604090206002815460ff1660038111156109b8576109b861362e565b146109d657604051634f4b461f60e11b815260040160405180910390fd5b6004810154156109f95760405163632a22bb60e01b815260040160405180910390fd5b60068101548714610a425760405162461bcd60e51b815260206004820152601360248201527209cdec8ca40c6deeadce840dad2e6dac2e8c6d606b1b60448201526064016106d1565b83610a875760405162461bcd60e51b81526020600482015260156024820152741c1ad0dbdb5b5a5d1b595b9d081c995c5d5a5c9959605a1b60448201526064016106d1565b5f805460405163101bb4d760e21b8152600481018c90526001600160a01b039091169063406ed35c906024015f60405180830381865afa158015610acd573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610af491908101906137b0565b9050806101c0015115610bf35782610b3f5760405162461bcd60e51b815260206004820152600e60248201526d1c1c9bdbd9881c995c5d5a5c995960921b60448201526064016106d1565b8061012001516001600160a01b031663258ae5828686866040518463ffffffff1660e01b8152600401610b7493929190613936565b602060405180830381865afa158015610b8f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bb3919061394f565b610bf35760405162461bcd60e51b815260206004820152601160248201527024b73b30b634b2102225a390383937b7b360791b60448201526064016106d1565b60048281018690555f8b815260096020526040808220889055905490516340a3b76160e11b81529182018c9052602482018790526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610c51575f5ffd5b505af1158015610c63573d5f5f3e3d5ffd5b50505050897fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f8a8a8a8a8a8a8a604051610ca39796959493929190613968565b60405180910390a250505050505050505050565b5f818152600a6020526040812081815460ff166003811115610cdb57610cdb61362e565b03610cf957604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610d2e5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610d5257610d5261362e565b14610d70576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610db7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ddb91906139e8565b905080610dee6040860160208701613a12565b63ffffffff161115610e066040860160208701613a12565b829091610e34576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106d1565b5050815460ff1916600190811783558201859055436002830155600354610e5b9042613a2b565b6003830155610e6f60058301856002613213565b50610e78611b9a565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ec9928a928a9291613a3e565b60405180910390a250600195945050505050565b5f610ee7826113af565b610ef257505f919050565b6001546001600160a01b0316610f1b576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610f4b90859060040161356d565b602060405180830381865afa158015610f66573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f8a919061394f565b92915050565b5f818152600a602052604081206001815460ff166003811115610fb557610fb561362e565b14610fc257505f92915050565b6003015442111592915050565b5f60015f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff16600281111561100b5761100b61362e565b149392505050565b61101b611e88565b6001600160a01b0381166110425760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b611091611e88565b61109a5f611eba565b565b600b545f9081906001600160a01b031633146110cb5760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff1660038111156110f0576110f061362e565b1461110e57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f90815260098301602052604090205463ffffffff909116925060019060ff16600281111561114e5761114e61362e565b1461115e57600a0154915061123b565b6001600160a01b0385165f9081526009820160205260408120805460ff19166002179055600a8201805491611192836135f1565b919050555080600a01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b84986866040516111e3929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b61124b611381565b6001600160a01b0316336001600160a01b0316148061127457506001546001600160a01b031633145b61129157604051632864c4e160e01b815260040160405180910390fd5b61129a816113af565b61137e5760048054600160281b900464ffffffffff16906112c4906001600160a01b038416611f2a565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161131583613642565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161079a565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a602052604090206004810154606091906113ff576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561145557602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611437575b5050505050915050919050565b5f805f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff16600281111561149d5761149d61362e565b14159392505050565b6114ae611e88565b6001600160a01b0381166114d55760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b5f611500612100565b805490915060ff600160401b82041615906001600160401b03165f811580156115265750825b90505f826001600160401b031660011480156115415750303b155b90508115801561154f575080155b1561156d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561159757845460ff60401b1916600160401b1785555b6001600160a01b0387166115be5760405163d92e233d60e01b815260040160405180910390fd5b6115c733612128565b6115d360046014612139565b6115dc866107a6565b6115e4611381565b6001600160a01b0316876001600160a01b0316146116055761160587611bac565b831561164b57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff1660038111156116785761167861362e565b0361169657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116ae576116ae61362e565b146116cc57604051631860f69960e31b815260040160405180910390fd5b806003015442116116f057604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff161115806117d5578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b1580156117b6575f5ffd5b505af11580156117c8573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600a83018190555f816001600160401b0381111561180557611805613606565b60405190808252806020026020018201604052801561182e578160200160208202803683370190505b5090505f5b828110156118a057846008015f8660060183815481106118555761185561361a565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054825183908390811061188d5761188d61361a565b6020908102919091010152600101611833565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b1580156118e3575f5ffd5b505af11580156118f5573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ec9929190613a8e565b611936611e88565b6001600160a01b03811661195d5760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff1660038111156119ca576119ca61362e565b036119e857604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611a0057611a0061362e565b14611a1e57604051631860f69960e31b815260040160405180910390fd5b8060030154421115611a4357604051639a19114d60e01b815260040160405180910390fd5b335f90815260078201602052604090205460ff1615611a755760405163257309f160e11b815260040160405180910390fd5b611a7e33610edd565b611a9b5760405163149fbcfd60e11b815260040160405180910390fd5b611aa63383856121b8565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526007850160205260409020805460ff19166001179055909150611b2590839083612389565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611b95576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611ba76004601461258a565b905090565b611bb4611e88565b6001600160a01b038116611bdd575f604051631e4fbdf760e01b81526004016106d1919061356d565b61137e81611eba565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611c255760405162461bcd60e51b81526004016106d190613af1565b825464ffffffffff600160281b90910481169082168111611c835760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106d1565b825f5b81866001015f611c968488612683565b64ffffffffff1681526020019081526020015f20819055505f816001611cbc9190613b3b565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611cf15750611e80565b600185165f03611db8575f611d1083611d0b886001613b54565b612683565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611d7191600401613b71565b602060405180830381865af4158015611d8c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611db091906139e8565b935050611e6c565b5f611dc883611d0b600189613ba1565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e2991600401613b71565b602060405180830381865af4158015611e44573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e6891906139e8565b9350505b50647fffffffff600194851c169301611c86565b505050505050565b33611e91611381565b6001600160a01b03161461109a573360405163118cdaa760e01b81526004016106d1919061356d565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611f795760405162461bcd60e51b81526004016106d190613af1565b825464ffffffffff90811690821610611fcc5760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106d1565b611fd7816001613b54565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f61200e8487612683565b64ffffffffff16815260208101919091526040015f205560018316156120f9575f61203e82611d0b600187613ba1565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161209f91600401613b71565b602060405180830381865af41580156120ba573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120de91906139e8565b647fffffffff600195861c1694909350919091019050611ffe565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610f8a565b6121306126a0565b61137e816126c5565b602060ff821611156121875760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106d1565b612198600160ff831681901b613bbe565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116121d85760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612201576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161223791613bbe565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa15801561227e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122a291906139e8565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122f5573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061231991906139e8565b90505f811161233b5760405163aeaddff160e01b815260040160405180910390fd5b5f6123468284613bd1565b90505f81116123685760405163149fbcfd60e11b815260040160405180910390fd5b8086111561164b5760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff169081111561240757508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526008870182526040808420869055600988019092529120805460ff1916821790559050612583565b5f5f90505f876008015f855f815481106124235761242361361a565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b84548110156124ab575f896008015f87848154811061246d5761246d61361a565b5f9182526020808320909101546001600160a01b031683528201929092526040019020549050828111156124a2578092508193505b5060010161244c565b508086106124bf575f945050505050612583565b5f886009015f8685815481106124d7576124d761361a565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156125145761251461362e565b02179055508684838154811061252c5761252c61361a565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260088a018252604080822089905560098b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff16116125dd5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106d1565b602060ff831611156126015760405162461bcd60e51b81526004016106d190613bf0565b8254600160281b900464ffffffffff168061262060ff85166002613d41565b64ffffffffff1610156126705760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106d1565b61267b8482856126cd565b949350505050565b5f8161269660ff851663ffffffff613d5a565b6125839190613b54565b6126a8612795565b61109a57604051631afcd79f60e31b815260040160405180910390fd5b611bb46126a0565b5f602060ff831611156126f25760405162461bcd60e51b81526004016106d190613bf0565b8264ffffffffff165f0361271057612709826127ae565b9050612583565b5f61271c836001613b3b565b60ff166001600160401b0381111561273657612736613606565b60405190808252806020026020018201604052801561275f578160200160208202803683370190505b50905061276e85858584612e48565b808360ff16815181106127835761278361361a565b60200260200101519150509392505050565b5f61279e612100565b54600160401b900460ff16919050565b5f8160ff165f036127c057505f919050565b8160ff166001036127f257507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361282457507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361285657507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361288857507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036128ba57507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036128ec57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361291e57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361295057507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361298257507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036129b457507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036129e657507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612a1857507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612a4a57507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612a7c57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612aae57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612ae057507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612b1257507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612b4457507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612b7657507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612ba857507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612bda57507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612c0c57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612c3e57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612c7057507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612ca257507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612cd457507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612d0657507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612d3857507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612d6a57507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612d9c57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612dce57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612e0057507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106d1565b602060ff83161115612e6c5760405162461bcd60e51b81526004016106d190613bf0565b5f8364ffffffffff1611612ed05760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106d1565b5f612edc600185613ba1565b9050600181165f03612f2f57846001015f612ef75f84612683565b64ffffffffff1681526020019081526020015f2054825f81518110612f1e57612f1e61361a565b602002602001018181525050612f57565b612f385f6127ae565b825f81518110612f4a57612f4a61361a565b6020026020010181815250505b5f5b8360ff168160ff161015611e8057600182165f0361304f5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612fab57612fab61361a565b60200260200101518152602001612fc1856127ae565b8152506040518263ffffffff1660e01b8152600401612fe09190613b71565b602060405180830381865af4158015612ffb573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061301f91906139e8565b8361302b836001613b3b565b60ff168151811061303e5761303e61361a565b602002602001018181525050613200565b5f61305b826001613b3b565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156130fd575f876001015f6130b28560016130a19190613b3b565b60018864ffffffffff16901c612683565b64ffffffffff1681526020019081526020015f2054905080858460016130d89190613b3b565b60ff16815181106130eb576130eb61361a565b602002602001018181525050506131fe565b5f876001015f61311485600188611d0b9190613ba1565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff168151811061316b5761316b61361a565b60200260200101518152506040518263ffffffff1660e01b81526004016131929190613b71565b602060405180830381865af41580156131ad573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131d191906139e8565b856131dd856001613b3b565b60ff16815181106131f0576131f061361a565b602002602001018181525050505b505b647fffffffff600192831c169101612f59565b6001830191839082156132a4579160200282015f5b8382111561327257833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613228565b80156132a25782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613272565b505b506132b09291506132b4565b5090565b5b808211156132b0575f81556001016132b5565b6001600160a01b038116811461137e575f5ffd5b5f602082840312156132ec575f5ffd5b8135612583816132c8565b5f60208284031215613307575f5ffd5b5035919050565b5f8151808452602084019350602083015f5b828110156133475781516001600160a01b0316865260209586019590910190600101613320565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613347578151865260209586019590910190600101613363565b604081525f613393604083018561330e565b82810360208401526133a58185613351565b95945050505050565b5f5f83601f8401126133be575f5ffd5b5081356001600160401b038111156133d4575f5ffd5b6020830191508360208285010111156133eb575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b031215613409575f5ffd5b8835975060208901356001600160401b03811115613425575f5ffd5b8901601f81018b13613435575f5ffd5b80356001600160401b0381111561344a575f5ffd5b8b60208260051b840101111561345e575f5ffd5b6020919091019750955060408901356001600160401b03811115613480575f5ffd5b61348c8b828c016133ae565b9096509450506060890135925060808901356001600160401b038111156134b1575f5ffd5b6134bd8b828c016133ae565b999c989b5096995094979396929594505050565b5f5f5f608084860312156134e3575f5ffd5b8335925060208401359150608084018510156134fd575f5ffd5b6040840190509250925092565b5f5f6040838503121561351b575f5ffd5b82359150602083013561352d816132c8565b809150509250929050565b5f5f5f6060848603121561354a575f5ffd5b83359250602084013561355c816132c8565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f612583602083018461330e565b5f5f604083850312156135a4575f5ffd5b82356135af816132c8565b946020939093013593505050565b5f5f604083850312156135ce575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f816135ff576135ff6135dd565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f60018201613653576136536135dd565b5060010190565b6040516101e081016001600160401b038111828210171561367d5761367d613606565b60405290565b805160048110611b95575f5ffd5b5f82601f8301126136a0575f5ffd5b604080519081016001600160401b03811182821017156136c2576136c2613606565b80604052508060408401858111156136d8575f5ffd5b845b818110156136f25780518352602092830192016136da565b509195945050505050565b8051611b95816132c8565b805160ff81168114611b95575f5ffd5b5f82601f830112613727575f5ffd5b81516001600160401b0381111561374057613740613606565b604051601f8201601f19908116603f011681016001600160401b038111828210171561376e5761376e613606565b604052818152838201602001851015613785575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114611b95575f5ffd5b5f602082840312156137c0575f5ffd5b81516001600160401b038111156137d5575f5ffd5b820161020081850312156137e7575f5ffd5b6137ef61365a565b815181526137ff60208301613683565b60208201526040828101519082015261381b8560608401613691565b606082015260a0820151608082015261383660c083016136fd565b60a082015261384760e08301613708565b60c08201526101008201516001600160401b03811115613865575f5ffd5b61387186828501613718565b60e08301525061388461012083016136fd565b61010082015261389761014083016136fd565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b038111156138cd575f5ffd5b6138d986828501613718565b610180830152506138ed6101c083016136fd565b6101a08201526139006101e083016137a1565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b838152604060208201525f6133a560408301848661390e565b5f6020828403121561395f575f5ffd5b612583826137a1565b608080825281018790525f8860a08301825b8a8110156139aa57823561398d816132c8565b6001600160a01b031682526020928301929091019060010161397a565b5083810360208501526139be81898b61390e565b91505085604084015282810360608401526139da81858761390e565b9a9950505050505050505050565b5f602082840312156139f8575f5ffd5b5051919050565b803563ffffffff81168114611b95575f5ffd5b5f60208284031215613a22575f5ffd5b612583826139ff565b80820180821115610f8a57610f8a6135dd565b84815260a0810160208201855f5b6002811015613a795763ffffffff613a63836139ff565b1683526020928301929190910190600101613a4c565b50505060608201939093526080015292915050565b604080825283549082018190525f8481526020812090916060840190835b81811015613ad35783546001600160a01b0316835260019384019360209093019201613aac565b50508381036020850152613ae78186613351565b9695505050505050565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610f8a57610f8a6135dd565b64ffffffffff8181168382160190811115610f8a57610f8a6135dd565b6040810181835f5b6002811015613b98578151835260209283019290910190600101613b79565b50505092915050565b64ffffffffff8281168282160390811115610f8a57610f8a6135dd565b81810381811115610f8a57610f8a6135dd565b5f82613beb57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b600184111561123b57808504811115613c5257613c526135dd565b6001841615613c6057908102905b60019390931c928002613c37565b5f82613c7c57506001610f8a565b81613c8857505f610f8a565b8160018114613c9e5760028114613ca857613cda565b6001915050610f8a565b60ff841115613cb957613cb96135dd565b6001841b915064ffffffffff821115613cd457613cd46135dd565b50610f8a565b5060208310610133831016604e8410600b8410161715613d12575081810a64ffffffffff811115613d0d57613d0d6135dd565b610f8a565b613d2264ffffffffff8484613c33565b8064ffffffffff04821115613d3957613d396135dd565b029392505050565b5f61258364ffffffffff841664ffffffffff8416613c6e565b64ffffffffff8181168382160290811690818114613d7a57613d7a6135dd565b509291505056fea164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b506004361061023f575f3560e01c80639a7a2ffc11610135578063dbb06c93116100b4578063f165053611610079578063f165053614610578578063f2fde38b14610592578063f379b0df146105a5578063f52fd803146105df578063f6fc05d514610651575f5ffd5b8063dbb06c9314610525578063e59e469514610537578063e6745e131461054a578063e82f3b701461055d578063ebf0c71714610570575f5ffd5b8063c2b40ae4116100fa578063c2b40ae4146104b0578063c3a0ec30146104cf578063ca2869a0146104e0578063cd6dc687146104ff578063da881e5a14610512575f5ffd5b80639a7a2ffc146104255780639f0f874a14610461578063a01649301461046a578063a8a4d69b1461048a578063bff232c11461049d575f5ffd5b806370e36bbe116101c15780638cb89ecb116101865780638cb89ecb146103c25780638d1ddfb1146103e15780638da5cb5b146103f75780638e5ce3ad146103ff5780639015d37114610412575f5ffd5b806370e36bbe14610347578063715018a61461035a5780637c92f52414610362578063858142431461038f5780638a78bb15146103af575f5ffd5b8063291a691b11610207578063291a691b146102c95780632e7b716d146102ec5780634d6861a6146102ff57806350e6d94c146103125780635d50477614610334575f5ffd5b8063096b810a146102435780630f3e34121461025857806317d611201461026b5780631cc321b5146102955780632800d829146102a8575b5f5ffd5b6102566102513660046132dc565b61065a565b005b6102566102663660046132f7565b6107a6565b61027e6102793660046132f7565b6107e9565b60405161028c929190613381565b60405180910390f35b6102566102a33660046133f2565b610993565b6102bb6102b63660046132f7565b610cb7565b60405190815260200161028c565b6102dc6102d73660046134d1565b610d03565b604051901515815260200161028c565b6102dc6102fa3660046132dc565b610edd565b6102dc61030d3660046132f7565b610f90565b6102dc6103203660046132dc565b60066020525f908152604090205460ff1681565b6102dc61034236600461350a565b610fcf565b6102566103553660046132dc565b611013565b610256611089565b610375610370366004613538565b61109c565b6040805192835263ffffffff90911660208301520161028c565b6001546103a2906001600160a01b031681565b60405161028c919061356d565b6102566103bd3660046132dc565b611243565b6102bb6103d03660046132f7565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102bb565b6103a2611381565b600b546103a2906001600160a01b031681565b6102dc6104203660046132dc565b6113af565b61044b6104333660046132dc565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161028c565b6102bb60035481565b61047d6104783660046132f7565b6113cc565b60405161028c9190613581565b6102dc61049836600461350a565b611462565b6102566104ab3660046132dc565b6114a6565b6102bb6104be3660046132f7565b60086020525f908152604090205481565b6001546001600160a01b03166103a2565b6102bb6104ee3660046132f7565b5f9081526008602052604090205490565b61025661050d366004613593565b6114f7565b6102dc6105203660046132f7565b611654565b5f546103a2906001600160a01b031681565b6102566105453660046132dc565b61192e565b6102566105583660046135bd565b6119a6565b6102bb61056b3660046132f7565b611b69565b6102bb611b9a565b610580601481565b60405160ff909116815260200161028c565b6102566105a03660046132dc565b611bac565b6004546105c19064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161028c565b6106226105ed3660046132f7565b5f908152600a6020819052604090912090810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161028c949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102bb60025481565b610662611381565b6001600160a01b0316336001600160a01b0316148061068b57506001546001600160a01b031633145b6106a857604051632864c4e160e01b815260040160405180910390fd5b6106b1816113af565b81906106da576040516381e5828960e01b81526004016106d1919061356d565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107089060049083611be6565b6001600160a01b0382165f908152600660205260408120805460ff191690556002805491610735836135f1565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b6107ae611e88565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a60208190526040909120600681015491810154606092839291806001600160401b0381111561082057610820613606565b604051908082528060200260200182016040528015610849578160200160208202803683370190505b509450806001600160401b0381111561086457610864613606565b60405190808252806020026020018201604052801561088d578160200160208202803683370190505b5093505f805b83811015610989575f8560060182815481106108b1576108b161361a565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f90815260098801602052604090205460ff1660028111156108f7576108f761362e565b03610980578088848151811061090f5761090f61361a565b60200260200101906001600160a01b031690816001600160a01b031681525050856008015f826001600160a01b03166001600160a01b031681526020019081526020015f20548784815181106109675761096761361a565b60209081029190910101528261097c81613642565b9350505b50600101610893565b5050505050915091565b5f888152600a602052604090206002815460ff1660038111156109b8576109b861362e565b146109d657604051634f4b461f60e11b815260040160405180910390fd5b6004810154156109f95760405163632a22bb60e01b815260040160405180910390fd5b60068101548714610a425760405162461bcd60e51b815260206004820152601360248201527209cdec8ca40c6deeadce840dad2e6dac2e8c6d606b1b60448201526064016106d1565b83610a875760405162461bcd60e51b81526020600482015260156024820152741c1ad0dbdb5b5a5d1b595b9d081c995c5d5a5c9959605a1b60448201526064016106d1565b5f805460405163101bb4d760e21b8152600481018c90526001600160a01b039091169063406ed35c906024015f60405180830381865afa158015610acd573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610af491908101906137b0565b9050806101c0015115610bf35782610b3f5760405162461bcd60e51b815260206004820152600e60248201526d1c1c9bdbd9881c995c5d5a5c995960921b60448201526064016106d1565b8061012001516001600160a01b031663258ae5828686866040518463ffffffff1660e01b8152600401610b7493929190613936565b602060405180830381865afa158015610b8f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bb3919061394f565b610bf35760405162461bcd60e51b815260206004820152601160248201527024b73b30b634b2102225a390383937b7b360791b60448201526064016106d1565b60048281018690555f8b815260096020526040808220889055905490516340a3b76160e11b81529182018c9052602482018790526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610c51575f5ffd5b505af1158015610c63573d5f5f3e3d5ffd5b50505050897fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f8a8a8a8a8a8a8a604051610ca39796959493929190613968565b60405180910390a250505050505050505050565b5f818152600a6020526040812081815460ff166003811115610cdb57610cdb61362e565b03610cf957604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610d2e5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610d5257610d5261362e565b14610d70576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610db7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ddb91906139e8565b905080610dee6040860160208701613a12565b63ffffffff161115610e066040860160208701613a12565b829091610e34576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106d1565b5050815460ff1916600190811783558201859055436002830155600354610e5b9042613a2b565b6003830155610e6f60058301856002613213565b50610e78611b9a565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ec9928a928a9291613a3e565b60405180910390a250600195945050505050565b5f610ee7826113af565b610ef257505f919050565b6001546001600160a01b0316610f1b576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610f4b90859060040161356d565b602060405180830381865afa158015610f66573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f8a919061394f565b92915050565b5f818152600a602052604081206001815460ff166003811115610fb557610fb561362e565b14610fc257505f92915050565b6003015442111592915050565b5f60015f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff16600281111561100b5761100b61362e565b149392505050565b61101b611e88565b6001600160a01b0381166110425760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b611091611e88565b61109a5f611eba565b565b600b545f9081906001600160a01b031633146110cb5760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff1660038111156110f0576110f061362e565b1461110e57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f90815260098301602052604090205463ffffffff909116925060019060ff16600281111561114e5761114e61362e565b1461115e57600a0154915061123b565b6001600160a01b0385165f9081526009820160205260408120805460ff19166002179055600a8201805491611192836135f1565b919050555080600a01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b84986866040516111e3929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b61124b611381565b6001600160a01b0316336001600160a01b0316148061127457506001546001600160a01b031633145b61129157604051632864c4e160e01b815260040160405180910390fd5b61129a816113af565b61137e5760048054600160281b900464ffffffffff16906112c4906001600160a01b038416611f2a565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161131583613642565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161079a565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a602052604090206004810154606091906113ff576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561145557602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611437575b5050505050915050919050565b5f805f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff16600281111561149d5761149d61362e565b14159392505050565b6114ae611e88565b6001600160a01b0381166114d55760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b5f611500612100565b805490915060ff600160401b82041615906001600160401b03165f811580156115265750825b90505f826001600160401b031660011480156115415750303b155b90508115801561154f575080155b1561156d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561159757845460ff60401b1916600160401b1785555b6001600160a01b0387166115be5760405163d92e233d60e01b815260040160405180910390fd5b6115c733612128565b6115d360046014612139565b6115dc866107a6565b6115e4611381565b6001600160a01b0316876001600160a01b0316146116055761160587611bac565b831561164b57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff1660038111156116785761167861362e565b0361169657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116ae576116ae61362e565b146116cc57604051631860f69960e31b815260040160405180910390fd5b806003015442116116f057604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff161115806117d5578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b1580156117b6575f5ffd5b505af11580156117c8573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600a83018190555f816001600160401b0381111561180557611805613606565b60405190808252806020026020018201604052801561182e578160200160208202803683370190505b5090505f5b828110156118a057846008015f8660060183815481106118555761185561361a565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054825183908390811061188d5761188d61361a565b6020908102919091010152600101611833565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b1580156118e3575f5ffd5b505af11580156118f5573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ec9929190613a8e565b611936611e88565b6001600160a01b03811661195d5760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff1660038111156119ca576119ca61362e565b036119e857604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611a0057611a0061362e565b14611a1e57604051631860f69960e31b815260040160405180910390fd5b8060030154421115611a4357604051639a19114d60e01b815260040160405180910390fd5b335f90815260078201602052604090205460ff1615611a755760405163257309f160e11b815260040160405180910390fd5b611a7e33610edd565b611a9b5760405163149fbcfd60e11b815260040160405180910390fd5b611aa63383856121b8565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526007850160205260409020805460ff19166001179055909150611b2590839083612389565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611b95576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611ba76004601461258a565b905090565b611bb4611e88565b6001600160a01b038116611bdd575f604051631e4fbdf760e01b81526004016106d1919061356d565b61137e81611eba565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611c255760405162461bcd60e51b81526004016106d190613af1565b825464ffffffffff600160281b90910481169082168111611c835760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106d1565b825f5b81866001015f611c968488612683565b64ffffffffff1681526020019081526020015f20819055505f816001611cbc9190613b3b565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611cf15750611e80565b600185165f03611db8575f611d1083611d0b886001613b54565b612683565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611d7191600401613b71565b602060405180830381865af4158015611d8c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611db091906139e8565b935050611e6c565b5f611dc883611d0b600189613ba1565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e2991600401613b71565b602060405180830381865af4158015611e44573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e6891906139e8565b9350505b50647fffffffff600194851c169301611c86565b505050505050565b33611e91611381565b6001600160a01b03161461109a573360405163118cdaa760e01b81526004016106d1919061356d565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611f795760405162461bcd60e51b81526004016106d190613af1565b825464ffffffffff90811690821610611fcc5760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106d1565b611fd7816001613b54565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f61200e8487612683565b64ffffffffff16815260208101919091526040015f205560018316156120f9575f61203e82611d0b600187613ba1565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161209f91600401613b71565b602060405180830381865af41580156120ba573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120de91906139e8565b647fffffffff600195861c1694909350919091019050611ffe565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610f8a565b6121306126a0565b61137e816126c5565b602060ff821611156121875760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106d1565b612198600160ff831681901b613bbe565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116121d85760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612201576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161223791613bbe565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa15801561227e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122a291906139e8565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122f5573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061231991906139e8565b90505f811161233b5760405163aeaddff160e01b815260040160405180910390fd5b5f6123468284613bd1565b90505f81116123685760405163149fbcfd60e11b815260040160405180910390fd5b8086111561164b5760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff169081111561240757508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526008870182526040808420869055600988019092529120805460ff1916821790559050612583565b5f5f90505f876008015f855f815481106124235761242361361a565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b84548110156124ab575f896008015f87848154811061246d5761246d61361a565b5f9182526020808320909101546001600160a01b031683528201929092526040019020549050828111156124a2578092508193505b5060010161244c565b508086106124bf575f945050505050612583565b5f886009015f8685815481106124d7576124d761361a565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156125145761251461362e565b02179055508684838154811061252c5761252c61361a565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260088a018252604080822089905560098b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff16116125dd5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106d1565b602060ff831611156126015760405162461bcd60e51b81526004016106d190613bf0565b8254600160281b900464ffffffffff168061262060ff85166002613d41565b64ffffffffff1610156126705760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106d1565b61267b8482856126cd565b949350505050565b5f8161269660ff851663ffffffff613d5a565b6125839190613b54565b6126a8612795565b61109a57604051631afcd79f60e31b815260040160405180910390fd5b611bb46126a0565b5f602060ff831611156126f25760405162461bcd60e51b81526004016106d190613bf0565b8264ffffffffff165f0361271057612709826127ae565b9050612583565b5f61271c836001613b3b565b60ff166001600160401b0381111561273657612736613606565b60405190808252806020026020018201604052801561275f578160200160208202803683370190505b50905061276e85858584612e48565b808360ff16815181106127835761278361361a565b60200260200101519150509392505050565b5f61279e612100565b54600160401b900460ff16919050565b5f8160ff165f036127c057505f919050565b8160ff166001036127f257507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361282457507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361285657507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361288857507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036128ba57507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036128ec57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361291e57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361295057507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361298257507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036129b457507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036129e657507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612a1857507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612a4a57507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612a7c57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612aae57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612ae057507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612b1257507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612b4457507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612b7657507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612ba857507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612bda57507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612c0c57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612c3e57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612c7057507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612ca257507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612cd457507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612d0657507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612d3857507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612d6a57507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612d9c57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612dce57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612e0057507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106d1565b602060ff83161115612e6c5760405162461bcd60e51b81526004016106d190613bf0565b5f8364ffffffffff1611612ed05760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106d1565b5f612edc600185613ba1565b9050600181165f03612f2f57846001015f612ef75f84612683565b64ffffffffff1681526020019081526020015f2054825f81518110612f1e57612f1e61361a565b602002602001018181525050612f57565b612f385f6127ae565b825f81518110612f4a57612f4a61361a565b6020026020010181815250505b5f5b8360ff168160ff161015611e8057600182165f0361304f5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612fab57612fab61361a565b60200260200101518152602001612fc1856127ae565b8152506040518263ffffffff1660e01b8152600401612fe09190613b71565b602060405180830381865af4158015612ffb573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061301f91906139e8565b8361302b836001613b3b565b60ff168151811061303e5761303e61361a565b602002602001018181525050613200565b5f61305b826001613b3b565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156130fd575f876001015f6130b28560016130a19190613b3b565b60018864ffffffffff16901c612683565b64ffffffffff1681526020019081526020015f2054905080858460016130d89190613b3b565b60ff16815181106130eb576130eb61361a565b602002602001018181525050506131fe565b5f876001015f61311485600188611d0b9190613ba1565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff168151811061316b5761316b61361a565b60200260200101518152506040518263ffffffff1660e01b81526004016131929190613b71565b602060405180830381865af41580156131ad573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131d191906139e8565b856131dd856001613b3b565b60ff16815181106131f0576131f061361a565b602002602001018181525050505b505b647fffffffff600192831c169101612f59565b6001830191839082156132a4579160200282015f5b8382111561327257833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613228565b80156132a25782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613272565b505b506132b09291506132b4565b5090565b5b808211156132b0575f81556001016132b5565b6001600160a01b038116811461137e575f5ffd5b5f602082840312156132ec575f5ffd5b8135612583816132c8565b5f60208284031215613307575f5ffd5b5035919050565b5f8151808452602084019350602083015f5b828110156133475781516001600160a01b0316865260209586019590910190600101613320565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613347578151865260209586019590910190600101613363565b604081525f613393604083018561330e565b82810360208401526133a58185613351565b95945050505050565b5f5f83601f8401126133be575f5ffd5b5081356001600160401b038111156133d4575f5ffd5b6020830191508360208285010111156133eb575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b031215613409575f5ffd5b8835975060208901356001600160401b03811115613425575f5ffd5b8901601f81018b13613435575f5ffd5b80356001600160401b0381111561344a575f5ffd5b8b60208260051b840101111561345e575f5ffd5b6020919091019750955060408901356001600160401b03811115613480575f5ffd5b61348c8b828c016133ae565b9096509450506060890135925060808901356001600160401b038111156134b1575f5ffd5b6134bd8b828c016133ae565b999c989b5096995094979396929594505050565b5f5f5f608084860312156134e3575f5ffd5b8335925060208401359150608084018510156134fd575f5ffd5b6040840190509250925092565b5f5f6040838503121561351b575f5ffd5b82359150602083013561352d816132c8565b809150509250929050565b5f5f5f6060848603121561354a575f5ffd5b83359250602084013561355c816132c8565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f612583602083018461330e565b5f5f604083850312156135a4575f5ffd5b82356135af816132c8565b946020939093013593505050565b5f5f604083850312156135ce575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f816135ff576135ff6135dd565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f60018201613653576136536135dd565b5060010190565b6040516101e081016001600160401b038111828210171561367d5761367d613606565b60405290565b805160048110611b95575f5ffd5b5f82601f8301126136a0575f5ffd5b604080519081016001600160401b03811182821017156136c2576136c2613606565b80604052508060408401858111156136d8575f5ffd5b845b818110156136f25780518352602092830192016136da565b509195945050505050565b8051611b95816132c8565b805160ff81168114611b95575f5ffd5b5f82601f830112613727575f5ffd5b81516001600160401b0381111561374057613740613606565b604051601f8201601f19908116603f011681016001600160401b038111828210171561376e5761376e613606565b604052818152838201602001851015613785575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114611b95575f5ffd5b5f602082840312156137c0575f5ffd5b81516001600160401b038111156137d5575f5ffd5b820161020081850312156137e7575f5ffd5b6137ef61365a565b815181526137ff60208301613683565b60208201526040828101519082015261381b8560608401613691565b606082015260a0820151608082015261383660c083016136fd565b60a082015261384760e08301613708565b60c08201526101008201516001600160401b03811115613865575f5ffd5b61387186828501613718565b60e08301525061388461012083016136fd565b61010082015261389761014083016136fd565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b038111156138cd575f5ffd5b6138d986828501613718565b610180830152506138ed6101c083016136fd565b6101a08201526139006101e083016137a1565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b838152604060208201525f6133a560408301848661390e565b5f6020828403121561395f575f5ffd5b612583826137a1565b608080825281018790525f8860a08301825b8a8110156139aa57823561398d816132c8565b6001600160a01b031682526020928301929091019060010161397a565b5083810360208501526139be81898b61390e565b91505085604084015282810360608401526139da81858761390e565b9a9950505050505050505050565b5f602082840312156139f8575f5ffd5b5051919050565b803563ffffffff81168114611b95575f5ffd5b5f60208284031215613a22575f5ffd5b612583826139ff565b80820180821115610f8a57610f8a6135dd565b84815260a0810160208201855f5b6002811015613a795763ffffffff613a63836139ff565b1683526020928301929190910190600101613a4c565b50505060608201939093526080015292915050565b604080825283549082018190525f8481526020812090916060840190835b81811015613ad35783546001600160a01b0316835260019384019360209093019201613aac565b50508381036020850152613ae78186613351565b9695505050505050565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610f8a57610f8a6135dd565b64ffffffffff8181168382160190811115610f8a57610f8a6135dd565b6040810181835f5b6002811015613b98578151835260209283019290910190600101613b79565b50505092915050565b64ffffffffff8281168282160390811115610f8a57610f8a6135dd565b81810381811115610f8a57610f8a6135dd565b5f82613beb57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b600184111561123b57808504811115613c5257613c526135dd565b6001841615613c6057908102905b60019390931c928002613c37565b5f82613c7c57506001610f8a565b81613c8857505f610f8a565b8160018114613c9e5760028114613ca857613cda565b6001915050610f8a565b60ff841115613cb957613cb96135dd565b6001841b915064ffffffffff821115613cd457613cd46135dd565b50610f8a565b5060208310610133831016604e8410600b8410161715613d12575081810a64ffffffffff811115613d0d57613d0d6135dd565b610f8a565b613d2264ffffffffff8484613c33565b8064ffffffffff04821115613d3957613d396135dd565b029392505050565b5f61258364ffffffffff841664ffffffffff8416613c6e565b64ffffffffff8181168382160290811690818114613d7a57613d7a6135dd565b509291505056fea164736f6c634300081c000a", "linkReferences": { "npm/poseidon-solidity@0.0.5/PoseidonT3.sol": { "PoseidonT3": [ { "length": 20, - "start": 7674 + "start": 7713 }, { "length": 20, - "start": 7858 + "start": 7897 }, { "length": 20, - "start": 8488 + "start": 8527 }, { "length": 20, - "start": 12322 + "start": 12361 }, { "length": 20, - "start": 12764 + "start": 12803 } ] } @@ -1297,28 +1303,28 @@ "PoseidonT3": [ { "length": 20, - "start": 7460 + "start": 7499 }, { "length": 20, - "start": 7644 + "start": 7683 }, { "length": 20, - "start": 8274 + "start": 8313 }, { "length": 20, - "start": 12108 + "start": 12147 }, { "length": 20, - "start": 12550 + "start": 12589 } ] } }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-ad0ab271a39c8d6cc271ed8cba75fab865dc3973" + "buildInfoId": "solc-0_8_28-64228f31c3990e4616cf0578598d186612e83409" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/Enclave.sol b/packages/enclave-contracts/contracts/Enclave.sol index 0bea3166d8..aba81feee8 100644 --- a/packages/enclave-contracts/contracts/Enclave.sol +++ b/packages/enclave-contracts/contracts/Enclave.sol @@ -76,7 +76,7 @@ contract Enclave is IEnclave, OwnableUpgradeable { mapping(bytes32 encryptionSchemeId => IDecryptionVerifier decryptionVerifier) public decryptionVerifiers; - /// @notice Mapping of encryption schemes to their C5 (pk_aggregation) proof verifiers. + /// @notice Mapping of encryption schemes to their DkgAggregator (EVM) proof verifiers. /// @dev Required per scheme; gates E3 requests like decryptionVerifier. mapping(bytes32 encryptionSchemeId => IPkVerifier pkVerifier) public pkVerifiers; @@ -388,8 +388,7 @@ contract Enclave is IEnclave, OwnableUpgradeable { function publishPlaintextOutput( uint256 e3Id, bytes calldata plaintextOutput, - bytes calldata proof, - bytes calldata foldProof + bytes calldata proof ) external returns (bool success) { E3 memory e3 = getE3(e3Id); @@ -413,10 +412,10 @@ contract Enclave is IEnclave, OwnableUpgradeable { _e3Stages[e3Id] = E3Stage.Complete; if (e3.proofAggregationEnabled) { - (success) = e3.decryptionVerifier.verify( + require(proof.length > 0, ProofRequired()); + success = e3.decryptionVerifier.verify( keccak256(plaintextOutput), - proof, - foldProof + proof ); require(success, InvalidOutput(plaintextOutput)); } else { diff --git a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol index d6a38193a5..2e629155bf 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -104,11 +104,14 @@ interface ICiphernodeRegistry { /// @notice This event MUST be emitted when a committee is selected for an E3. /// @param e3Id ID of the E3 for which the committee was selected. /// @param publicKey Public key of the committee. - /// @param proof C5 proof bytes verified prior to publication. + /// @param pkCommitment Hash-based aggregated PK commitment for the committee. + /// @param proof DkgAggregator (EVM) proof bytes verified prior to publication, + /// or empty bytes when proof aggregation is disabled for the E3. event CommitteePublished( uint256 indexed e3Id, address[] nodes, bytes publicKey, + bytes32 pkCommitment, bytes proof ); @@ -296,18 +299,22 @@ interface ICiphernodeRegistry { ) external returns (bool success); /// @notice Publishes the public key resulting from the committee selection process. - /// @dev This function MUST revert if not called by the owner. + /// @dev Permissionless once the committee is finalized. + /// When `e3.proofAggregationEnabled` is true, the `proof` is verified against + /// `pkCommitment` via the E3's pk verifier. When disabled, `proof` may be empty + /// and `pkCommitment` is trusted from the (signed) aggregator. /// @param e3Id ID of the E3 for which to select the committee. /// @param nodes Array of ciphernode addresses selected for the committee. /// @param publicKey The public key generated by the given committee. - /// @param proof C5 proof; ABI-encoded (bytes rawProof, bytes32[] publicInputs). Last input is aggregate commitment. - /// @param foldProof Optional fold proof ABI-encoded (bytes, bytes32[]); empty to skip. + /// @param pkCommitment Hash-based aggregated PK commitment for the committee. + /// @param proof DkgAggregator (EVM) proof ABI-encoded `(bytes rawProof, bytes32[] publicInputs)`, + /// or empty bytes when proof aggregation is disabled. function publishCommittee( uint256 e3Id, address[] calldata nodes, bytes calldata publicKey, - bytes calldata proof, - bytes calldata foldProof + bytes32 pkCommitment, + bytes calldata proof ) external; /// @notice This function should be called by the Enclave contract to get the public key of a committee. diff --git a/packages/enclave-contracts/contracts/interfaces/ICircuitVerifier.sol b/packages/enclave-contracts/contracts/interfaces/ICircuitVerifier.sol index d4551f4289..a9b7a29d97 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICircuitVerifier.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICircuitVerifier.sol @@ -7,7 +7,7 @@ pragma solidity >=0.8.27; /** * @title ICircuitVerifier - * @notice Interface for on-chain ZK circuit verifiers (e.g., ThresholdPkAggregationVerifier, Honk verifiers) + * @notice Interface for on-chain ZK circuit verifiers (e.g., DkgAggregatorVerifier, Honk verifiers) * @dev Standard interface matching the verification pattern used by Honk-generated verifiers. * Set the circuit verifier address directly as the proofVerifier in a SlashPolicy. */ diff --git a/packages/enclave-contracts/contracts/interfaces/IDecryptionVerifier.sol b/packages/enclave-contracts/contracts/interfaces/IDecryptionVerifier.sol index 2f4b78322c..8664408406 100644 --- a/packages/enclave-contracts/contracts/interfaces/IDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/interfaces/IDecryptionVerifier.sol @@ -7,19 +7,18 @@ pragma solidity >=0.8.27; /** * @title IDecryptionVerifier - * @notice Interface for verifying decrypted computation outputs - * @dev Implements cryptographic verification of plaintext outputs from encrypted computations + * @notice Interface for the DecryptionAggregator (EVM) proof verifier. + * @dev The DecryptionAggregator circuit internally verifies the C6-fold and C7 + * (decrypted_shares_aggregation) sub-proofs; this on-chain verifier only + * needs to verify the final EVM proof and bind it to the claimed plaintext. */ interface IDecryptionVerifier { - /// @notice Verify the decryption of a computation output - /// @dev This function is called by the Enclave contract when plaintext output is published - /// @param plaintextOutputHash The keccak256 hash of the plaintext output to be verified - /// @param proof ABI-encoded (bytes, bytes32[]) for C7. - /// @param foldProof ABI-encoded fold proof (bytes, bytes32[]) or empty to skip. - /// @return success Whether the plaintextOutputHash was successfully verified + /// @notice Verify a DecryptionAggregator EVM proof and bind it to `plaintextOutputHash`. + /// @param plaintextOutputHash `keccak256(plaintextOutput)` expected by the Enclave. + /// @param proof ABI-encoded `(bytes rawProof, bytes32[] publicInputs)`. + /// @return success True if the proof is valid and its embedded plaintext matches `plaintextOutputHash`. function verify( bytes32 plaintextOutputHash, - bytes memory proof, - bytes memory foldProof + bytes calldata proof ) external view returns (bool success); } diff --git a/packages/enclave-contracts/contracts/interfaces/IEnclave.sol b/packages/enclave-contracts/contracts/interfaces/IEnclave.sol index cb6c1dd58d..50084d8318 100644 --- a/packages/enclave-contracts/contracts/interfaces/IEnclave.sol +++ b/packages/enclave-contracts/contracts/interfaces/IEnclave.sol @@ -290,6 +290,9 @@ interface IEnclave { /// @param output The invalid output data. error InvalidOutput(bytes output); + /// @notice Thrown when proof aggregation is enabled but no proof was supplied. + error ProofRequired(); + /// @notice Thrown when the committee size has not been configured with thresholds. /// @param committeeSize The unconfigured committee size. error CommitteeSizeNotConfigured(CommitteeSize committeeSize); @@ -462,13 +465,13 @@ interface IEnclave { /// @dev This function MUST emit the PlaintextOutputPublished event. /// @param e3Id ID of the E3. /// @param plaintextOutput ABI encoded plaintext output. - /// @param proof ABI encoded data to verify the plaintextOutput (C7). - /// @param foldProof Optional fold proof ABI-encoded (bytes, bytes32[]); empty to skip. + /// @param proof DecryptionAggregator (EVM) proof ABI-encoded + /// `(bytes rawProof, bytes32[] publicInputs)`, or empty bytes when proof + /// aggregation is disabled for the E3. function publishPlaintextOutput( uint256 e3Id, bytes calldata plaintextOutput, - bytes calldata proof, - bytes calldata foldProof + bytes calldata proof ) external returns (bool success); //////////////////////////////////////////////////////////// diff --git a/packages/enclave-contracts/contracts/interfaces/IPkVerifier.sol b/packages/enclave-contracts/contracts/interfaces/IPkVerifier.sol index 41c8561ad4..69c62c61bf 100644 --- a/packages/enclave-contracts/contracts/interfaces/IPkVerifier.sol +++ b/packages/enclave-contracts/contracts/interfaces/IPkVerifier.sol @@ -7,17 +7,20 @@ pragma solidity >=0.8.27; /** * @title IPkVerifier - * @notice Interface for C5 (pk_aggregation) proof verification - * @dev Verifies that the aggregated committee public key was correctly reconstructed from party shares + * @notice Interface for the DkgAggregator (EVM) proof verifier. + * @dev The DkgAggregator circuit internally verifies the node-fold and C5 + * (pk_aggregation) sub-proofs; this on-chain verifier only needs to + * verify the final EVM proof and enforce that its last public input + * matches the committee's aggregated public-key commitment. */ interface IPkVerifier { - /// @notice Verify a C5 (pk_aggregation) proof and return the aggregate commitment. - /// @param proof ABI-encoded (bytes rawProof, bytes32[] publicInputs). - /// @param foldProof ABI-encoded fold proof (bytes, bytes32[]) or empty to skip. - /// @return pkCommitment The aggregate public key commitment (last public input). - /// @dev Reverts if the proof is invalid. + /// @notice Verify a DkgAggregator EVM proof and bind it to `pkCommitment`. + /// @param pkCommitment Hash-based aggregated PK commitment the proof must attest to + /// (equals `publicInputs[publicInputs.length - 1]`). + /// @param proof ABI-encoded `(bytes rawProof, bytes32[] publicInputs)`. + /// @return success True if the proof is valid and its last public input equals `pkCommitment`. function verify( - bytes memory proof, - bytes memory foldProof - ) external view returns (bytes32 pkCommitment); + bytes32 pkCommitment, + bytes calldata proof + ) external view returns (bool success); } diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index d5b51f4ecc..82660aa28e 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -188,20 +188,13 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { success = true; } - /// @notice Publishes a committee for an E3 computation - /// @dev Permissionless once the committee is finalized. Verification of C5 proof is - /// done in Enclave.onCommitteePublished. - /// @param e3Id ID of the E3 computation - /// @param nodes Array of ciphernode addresses selected for the committee - /// @param publicKey Aggregated public key of the committee - /// @param proof C5 proof; aggregate commitment extracted as last public input - /// @param foldProof Optional ABI-encoded fold proof (bytes, bytes32[]); empty to skip + /// @inheritdoc ICiphernodeRegistry function publishCommittee( uint256 e3Id, address[] calldata nodes, bytes calldata publicKey, - bytes calldata proof, - bytes calldata foldProof + bytes32 pkCommitment, + bytes calldata proof ) external { Committee storage c = committees[e3Id]; @@ -211,25 +204,23 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { ); require(c.publicKey == bytes32(0), CommitteeAlreadyPublished()); require(nodes.length == c.topNodes.length, "Node count mismatch"); - - (, bytes32[] memory publicInputs) = abi.decode( - proof, - (bytes, bytes32[]) - ); - require(publicInputs.length > 0, "C5: no public inputs"); - bytes32 publicKeyHash = publicInputs[publicInputs.length - 1]; + require(pkCommitment != bytes32(0), "pkCommitment required"); E3 memory e3 = enclave.getE3(e3Id); if (e3.proofAggregationEnabled) { - e3.pkVerifier.verify(proof, foldProof); + require(proof.length > 0, "proof required"); + require( + e3.pkVerifier.verify(pkCommitment, proof), + "Invalid DKG proof" + ); } - c.publicKey = publicKeyHash; - publicKeyHashes[e3Id] = publicKeyHash; + c.publicKey = pkCommitment; + publicKeyHashes[e3Id] = pkCommitment; - enclave.onCommitteePublished(e3Id, publicKeyHash); + enclave.onCommitteePublished(e3Id, pkCommitment); - emit CommitteePublished(e3Id, nodes, publicKey, proof); + emit CommitteePublished(e3Id, nodes, publicKey, pkCommitment, proof); } /// @inheritdoc ICiphernodeRegistry diff --git a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol index 545d61770e..2eaf2377db 100644 --- a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol @@ -70,7 +70,7 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { uint256, address[] calldata, bytes calldata, - bytes calldata, + bytes32, bytes calldata ) external pure {} // solhint-disable-line no-empty-blocks @@ -211,7 +211,7 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { uint256, address[] calldata, bytes calldata, - bytes calldata, + bytes32, bytes calldata ) external pure {} // solhint-disable-line no-empty-blocks diff --git a/packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol b/packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol index 65c868ca5b..da5c5ff257 100644 --- a/packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol @@ -8,13 +8,17 @@ pragma solidity >=0.8.27; import { IDecryptionVerifier } from "../interfaces/IDecryptionVerifier.sol"; contract MockDecryptionVerifier is IDecryptionVerifier { + /// @dev Test-only: proofs whose first 4 bytes are `0xdeadbeef` return false so + /// tests can exercise `InvalidOutput` when proof aggregation is on. + bytes4 private constant _FAIL_MAGIC = 0xdeadbeef; + function verify( bytes32, - bytes memory data, - bytes memory /* foldProof */ + bytes calldata proof ) external pure returns (bool success) { - data; - - if (data.length > 0) success = true; + if (proof.length >= 4 && bytes4(proof[0:4]) == _FAIL_MAGIC) { + return false; + } + success = proof.length > 0; } } diff --git a/packages/enclave-contracts/contracts/test/MockPkVerifier.sol b/packages/enclave-contracts/contracts/test/MockPkVerifier.sol index d7a57fe40f..0471c4116c 100644 --- a/packages/enclave-contracts/contracts/test/MockPkVerifier.sol +++ b/packages/enclave-contracts/contracts/test/MockPkVerifier.sol @@ -9,14 +9,14 @@ import { IPkVerifier } from "../interfaces/IPkVerifier.sol"; contract MockPkVerifier is IPkVerifier { function verify( - bytes memory proof, - bytes memory /* foldProof */ - ) external pure returns (bytes32 pkCommitment) { + bytes32 pkCommitment, + bytes calldata proof + ) external pure returns (bool) { (, bytes32[] memory publicInputs) = abi.decode( proof, (bytes, bytes32[]) ); - require(publicInputs.length > 0, "MockPkVerifier: no public inputs"); - return publicInputs[publicInputs.length - 1]; + if (publicInputs.length == 0) return false; + return publicInputs[publicInputs.length - 1] == pkCommitment; } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol index 3f762f0dc7..1520194ab3 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol @@ -10,34 +10,31 @@ import { ICircuitVerifier } from "../../interfaces/ICircuitVerifier.sol"; /** * @title BfvDecryptionVerifier - * @notice Decryption verifier for the fhe.rs:BFV encryption scheme. Verifies C7 - * (decrypted_shares_aggregation) proofs on-chain by delegating to the Honk - * ThresholdDecryptedSharesAggregationVerifier and validating that the - * plaintext extracted from public inputs matches the claimed hash. - * Optional `foldProof` is ABI-encoded (bytes, bytes32[]) for RecursiveAggregationFoldVerifier - * (C6 cross-node fold); pass empty bytes to skip. - * @dev Use this verifier when the Enclave is configured with encryptionSchemeId - * keccak256("fhe.rs:BFV"). Other encryption schemes will have their own verifiers. + * @notice Verifies the DecryptionAggregator (EVM) proof produced by the + * recursive aggregation pipeline (C6 folds + C7/decrypted_shares + * verified internally). Binds the proof to the claimed + * `plaintextOutputHash`. + * @dev Used when the Enclave is configured with encryptionSchemeId + * keccak256("fhe.rs:BFV"). The plaintext is exposed as the last + * `MESSAGE_COEFFS_COUNT` public inputs, matching + * `MAX_MSG_NON_ZERO_COEFFS` in the decryption_aggregator circuit. */ contract BfvDecryptionVerifier is IDecryptionVerifier { /// @dev Message is always the last 100 public inputs (100 uint64 coeffs = 800 bytes plaintext). - /// Layout-agnostic: works for prod and insecure circuit configs. uint256 constant MESSAGE_COEFFS_COUNT = 100; + /// @notice Underlying Honk verifier for the DecryptionAggregator circuit. ICircuitVerifier public immutable circuitVerifier; - ICircuitVerifier public immutable foldVerifier; - constructor(address _circuitVerifier, address _foldVerifier) { + constructor(address _circuitVerifier) { circuitVerifier = ICircuitVerifier(_circuitVerifier); - foldVerifier = ICircuitVerifier(_foldVerifier); } /// @inheritdoc IDecryptionVerifier function verify( bytes32 plaintextOutputHash, - bytes memory proof, - bytes memory foldProof - ) external view override returns (bool success) { + bytes calldata proof + ) external view override returns (bool) { (bytes memory rawProof, bytes32[] memory publicInputs) = abi.decode( proof, (bytes, bytes32[]) @@ -46,35 +43,10 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { if (publicInputs.length < MESSAGE_COEFFS_COUNT) { return false; } - - if (!circuitVerifier.verify(rawProof, publicInputs)) { - return false; - } - if (!_verifyPlaintextHash(publicInputs, plaintextOutputHash)) { return false; } - - if (!_verifyFold(foldProof)) { - return false; - } - - return true; - } - - function _verifyFold(bytes memory foldProof) internal view returns (bool) { - if (foldProof.length == 0) { - return true; - } - - (bytes memory foldRawProof, bytes32[] memory foldPublicInputs) = abi - .decode(foldProof, (bytes, bytes32[])); - - if (foldRawProof.length == 0 || foldPublicInputs.length == 0) { - return false; - } - - return foldVerifier.verify(foldRawProof, foldPublicInputs); + return circuitVerifier.verify(rawProof, publicInputs); } function _verifyPlaintextHash( diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol index 2ed2c7c51b..4b6e1ccf95 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol @@ -10,53 +10,37 @@ import { ICircuitVerifier } from "../../interfaces/ICircuitVerifier.sol"; /** * @title BfvPkVerifier - * @notice Verifies C5 (pk_aggregation) proofs on-chain. Delegates to the Honk - * ThresholdPkAggregationVerifier and returns the aggregate commitment from public inputs. - * Optional `foldProof` is ABI-encoded (bytes, bytes32[]) for RecursiveAggregationFoldVerifier - * (DKG cross-node fold); pass empty bytes to skip. - * @dev Use with encryptionSchemeId keccak256("fhe.rs:BFV"). Commitment count is enforced by the VK. + * @notice Verifies the DkgAggregator (EVM) proof produced by the recursive + * aggregation pipeline (node folds + C5/pk_aggregation verified + * internally). Binds the proof to a caller-supplied `pkCommitment`. + * @dev Used when the Enclave is configured with encryptionSchemeId + * keccak256("fhe.rs:BFV"). The aggregator circuit's last public input is + * the hash-based aggregated PK commitment. */ contract BfvPkVerifier is IPkVerifier { + /// @notice Underlying Honk verifier for the DkgAggregator circuit. ICircuitVerifier public immutable circuitVerifier; - ICircuitVerifier public immutable foldVerifier; - constructor(address _circuitVerifier, address _foldVerifier) { + constructor(address _circuitVerifier) { circuitVerifier = ICircuitVerifier(_circuitVerifier); - foldVerifier = ICircuitVerifier(_foldVerifier); } /// @inheritdoc IPkVerifier function verify( - bytes memory proof, - bytes memory foldProof - ) external view override returns (bytes32 pkCommitment) { + bytes32 pkCommitment, + bytes calldata proof + ) external view override returns (bool) { (bytes memory rawProof, bytes32[] memory publicInputs) = abi.decode( proof, (bytes, bytes32[]) ); - require(publicInputs.length > 0, "BfvPkVerifier: no public inputs"); - require( - circuitVerifier.verify(rawProof, publicInputs), - "BfvPkVerifier: invalid proof" - ); - - _verifyFold(foldProof); - - return publicInputs[publicInputs.length - 1]; - } - - function _verifyFold(bytes memory foldProof) internal view { - if (foldProof.length == 0) { - return; + if (publicInputs.length == 0) { + return false; } - - (bytes memory foldRawProof, bytes32[] memory foldPublicInputs) = abi - .decode(foldProof, (bytes, bytes32[])); - - require( - foldVerifier.verify(foldRawProof, foldPublicInputs), - "BfvPkVerifier: invalid fold proof" - ); + if (publicInputs[publicInputs.length - 1] != pkCommitment) { + return false; + } + return circuitVerifier.verify(rawProof, publicInputs); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/RecursiveAggregationFoldVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol similarity index 96% rename from packages/enclave-contracts/contracts/verifiers/bfv/honk/RecursiveAggregationFoldVerifier.sol rename to packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index c4f6834eeb..8f26000969 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/RecursiveAggregationFoldVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -7,8 +7,8 @@ pragma solidity >=0.8.21; uint256 constant N = 2097152; uint256 constant LOG_N = 21; -uint256 constant NUMBER_OF_PUBLIC_INPUTS = 18; -uint256 constant VK_HASH = 0x0c0bf44aa8d9785b43e16005da8fc455d330c7ff959979a9c7cc876703453859; +uint256 constant NUMBER_OF_PUBLIC_INPUTS = 125; +uint256 constant VK_HASH = 0x06bd2790bd0ebcdac35c8a64701d3ac99c897bfb988d6a9fa2b1f07a6630a73c; library HonkVerificationKey { function loadVerificationKey() internal @@ -18,149 +18,149 @@ library HonkVerificationKey { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), - publicInputsSize: uint256(18), + publicInputsSize: uint256(125), ql: Honk.G1Point({ x: uint256( - 0x04874f63e091441575d7f87163ad2d04fa3e4b49cee4cde183081c90e13ff17f + 0x117c944457d4c445ea9296e8369c57035aaa6aa1a8d8961945b3265da72a31bd ), y: uint256( - 0x1547db64a1675f5ab99f4dc4cf2af5cfd0ef1fbdb68977d8abc8f7112e88bf80 + 0x1085aa2ad3f5c33ba5b8281d03fc61950c0efb18edf3ca4bbacab6dac95eecc9 ) }), qr: Honk.G1Point({ x: uint256( - 0x17094eb4ca2210abcb1b4d41cf1355b04dd66b014d42345e6eadf8407a74ed1a + 0x2167efafb6f18a256ec49ac455de2b906ff483dd58e50c27a3eb01c965527b58 ), y: uint256( - 0x2228aebc15e7852110c94a633f8fb944d71cb1a5cff1b5127d0e4eecf1a7726f + 0x0e3673e8a48e9d2cdb54886bbd28ad0dd56f51ef8cb5b5b8d0c0586df815f0a0 ) }), qo: Honk.G1Point({ x: uint256( - 0x15cfc9a869b9c86a839a67ab04b780b3049b2d5f10a901055f85d4717428f6dc + 0x0a0501c4ac048110cf75f2b20f9ffdcd9a68876fd2e5d222b64c7bcac4915773 ), y: uint256( - 0x2aec2a8cee64e9da3981b9d6f9f00faffbe2ece25cd4f57d80b8552d81c716b5 + 0x06d4c7296e366ae470e2744f7c35c10a36205e875d76ff49b031643a34ca4b92 ) }), q4: Honk.G1Point({ x: uint256( - 0x232463dd05273276d135bcdfe18a8b299585d6e0fe220dfafe63a54293476dfe + 0x1cb87c34cda7588d707dfd9e5edc22aebcc4082a9a53bcd5cde3bee60468e8b0 ), y: uint256( - 0x17f763713bb623ce792152d0aabd32b83ca5a6abecb97ece41dc00a1bb6296ad + 0x05ff1a3b7cee6850d9d747a8da9eded3048f5d04bc0e3bc9430691c03e1e27c2 ) }), qm: Honk.G1Point({ x: uint256( - 0x286493b1bc5a9674ed3d063c79269512944b6dfb58b880361d36fce8a61c9814 + 0x202127ce81fb7649a03ec97775ca9fe2914220f389a449ddb636ba3d1176f3c3 ), y: uint256( - 0x1ef073ead7f7fd307a1a357548e4d8c9f5d03d5a7191cea80b2ba0cf03d18d29 + 0x267b8e078201ba78546404fa94714a5dcf759d079c3979f0a5ee5c132be90b45 ) }), qc: Honk.G1Point({ x: uint256( - 0x216ae5e3bf2a41f792d13f7028ec70fb78ac8d5fa4834341acad58d3453bf123 + 0x24252d386e1956c3f2eec5c2523dd7f1f00acab8486d728cda31a43a4c67ee03 ), y: uint256( - 0x2ef50ace63f2e23d4b2a184ce511e1b7781287457bcc135459c5c56de1877b93 + 0x0bfc7d6c95dd2d204a8535c6c39293700dc0a10b7c436927ea67d06c5e092b47 ) }), qLookup: Honk.G1Point({ x: uint256( - 0x1ad0852ae1f831506b33a76a4968849abaf87e068b8e00ba462e190ff4dc0ff9 + 0x2f67e65f1cc76c010e87bd861eff857a592e08ada281645410d67488ce521499 ), y: uint256( - 0x2b782e70b28e331338317e348ecb3d3ee69c0cb7b1ca634b32395b5349605a93 + 0x056e0fd959cdd56f2cb9d6d01440f7a5c2610ec3cb0efd64f856a119066366e9 ) }), qArith: Honk.G1Point({ x: uint256( - 0x1a03cf866e6a54045329039071909e3129fefe1a3f380a5db12e3156f6bed47e + 0x0061573aad5b769a27e0a025f41dc6916969da4cb5e543584ab86b72c93e3014 ), y: uint256( - 0x1bdf1ec93f5b5962d4aac343eaa211a5b8021546f0dc225d03f39cb465546d5a + 0x18285aff8bc7ed9f8cd1ea037443a178c0e77d7bae05c91613529a85717a5473 ) }), qDeltaRange: Honk.G1Point({ x: uint256( - 0x1940cce3f764969daf23bd6cf6d09ecd7e50392878ce2ba3a0a30c88f619cd37 + 0x11fe5aa4683da4b17c27c36019fd04b6c4daaf06d1138c136a82676d532e6897 ), y: uint256( - 0x06d183a757a5b2294f8e3b10d3304744bc4d7b68246b01563c48dfd3ab443842 + 0x244c605f6af78086d243fb2fffd815776932922d3cf081fa8d2ff7145b65dc0b ) }), qElliptic: Honk.G1Point({ x: uint256( - 0x1b5b7f1276e5867eef1d033df65d003c591c16bf64bd2d4cb89bccc1a98ab3dd + 0x0e6398a099538b9c1f74c3a86c63d474fd2b934772171c785230674a79962144 ), y: uint256( - 0x28f55668465e42d873d91d75906232c6f05bf37be890e4d4c0ef6a1d5df8c211 + 0x1152647b290d29791357e57cf4252e23aa8bb69266de667fcb6d49b50833c0fa ) }), qMemory: Honk.G1Point({ x: uint256( - 0x11a96a0fb134b4ad2a3ac7b54fc7989a18ada93ac73e0e7bfb7134f334854c55 + 0x2f77d58e1935021197cf807adc9d6ed89c208e7397f837811935ad8e04a25646 ), y: uint256( - 0x166911402df98ab7053779cb1f431384018e058cbe2ac79d1d5ff020ce5474d9 + 0x28ca0aea22ed60e0fda6a3087590deac2e3c6dbb39f672db617a2c8f7576a3a3 ) }), qNnf: Honk.G1Point({ x: uint256( - 0x11b342da18e6325f40bddb950e3ba058fe013e0a6757512f022a0d9961d3d468 + 0x1ba222b35b1b79a59d990df8d802bbffd526ed44aa573cb7de03b4926e33cfda ), y: uint256( - 0x04225227552dcaa71ff7c4571eebd04ca40091a33ff91d1b220af458dffca289 + 0x1e359c171885a972cb085a213975238e93b78b9b5067f6d470da0e47997cb560 ) }), qPoseidon2External: Honk.G1Point({ x: uint256( - 0x18ae1c0c284757fd6a6bcf60413a8e2334fcb46562ea30b9cb9444a663493463 + 0x1a8754cb4b93dbc97099cedc11c48286b8fb9ab103537f8872f0e44d05b6f677 ), y: uint256( - 0x1cade59f171ec1d94348309a02e1642067d6e6b43eb254837e703c1488dff221 + 0x25b8a35780ef8e6b2544f4b3c9b96d25df0f4f3f2eb652c0d3ba10ee3066dc06 ) }), qPoseidon2Internal: Honk.G1Point({ x: uint256( - 0x2537fc03ae8cfc49498d0a04704d77e10d6477d6b7c5e515cf24dadab0dee874 + 0x15dcd14e33a5c64ef3eef7938fccf400d798ec44f2a02fdf2e196dfa65f10c32 ), y: uint256( - 0x0850d14d62e2c194a69110f3aefa10d6caea9bf6ff940331877c5f49e41e5fa7 + 0x1d52fe9202be704fa45de182df518c960eca7b5c8c8f8f3ff92d6522b9efaa13 ) }), s1: Honk.G1Point({ x: uint256( - 0x02f24bc3b670a81abc44133de34592b13bb3a5ea67cdaa1d73ad35965b8abe49 + 0x168d4cfd6436be78fc713ef78e188f60e11008a4740fce4f64342bd82ced648b ), y: uint256( - 0x0c62df1ee51bfbaeba2a75c1cc8aa8a2ea3222f21bc0b8958bd43c3aa749e963 + 0x011b219a479e28c150896d9aed6042b687a10b2e3d725f5d08d7fea94a122f45 ) }), s2: Honk.G1Point({ x: uint256( - 0x21ac9dc80bd7f54e6817d224b7650c4176407a1109f92567c05676ac1fe0973c + 0x2e416ff405b6c53bc74973defa708ccc60c6fe491349100cec7b1481979da38e ), y: uint256( - 0x2d325ad91e20be879c716023bcde84d469c4f7867c12bb321c387e4a1036f86d + 0x14324c310091b0f31ddfcbbac474565ff58d178a307ffc8178cec436ccd7163c ) }), s3: Honk.G1Point({ x: uint256( - 0x25b123800356ccc6d7a609989a0e4fc132bf757389ca25f0c1db7612bbec2349 + 0x25133c6ea741cb3281d03c49f306b4f01fb6d338d27906e5409715ced74033f7 ), y: uint256( - 0x03832b0106e3581612f83d158277886cc9e5637790fccabde84b3218220f2a48 + 0x2a6c7c2db40a4d31607bd6f7d823207f1f5ed217b0784a085920722fd3ad36f4 ) }), s4: Honk.G1Point({ x: uint256( - 0x25962cb0996ebaeea7b01f8860fde2547f8bb738291e53c7f25a4ac80af4fd2e + 0x1609e2a7fef984bfb951cdaa3901af7d335fdde29d47d4cae8c3b9e7363134b4 ), y: uint256( - 0x22ec82955d2dc95fddc9cf9fd4f7aa555635abb02cf6eeb7ac3df225c8b62d52 + 0x1a6b3f2acc00e7396c9348d7dde2587bc1d0c8ee1b9c35465b02d16d5e23d3b7 ) }), t1: Honk.G1Point({ @@ -197,34 +197,34 @@ library HonkVerificationKey { }), id1: Honk.G1Point({ x: uint256( - 0x1332d280da268502ba2d853e14e53baf604c7a6a41b7e70bdf2e861cc6a59821 + 0x032c782b90a26fe68b4503eff33053fb0491f972c7589f678cecb1823567cfbb ), y: uint256( - 0x0d14761f5281d5dc1209b25146d92d9e5692294f818c8c3468c9921f7a7cce79 + 0x13cea8eda4216f5eec776c455ebe175fc799c21bfcc40bf06c386d575042624d ) }), id2: Honk.G1Point({ x: uint256( - 0x1b215ab72c8af91207dee6f8ac01f0e0d51895a9f670d7173c7b6d5b80a39085 + 0x1927315353225025dbf69ce7184ee16deb1f77c740b23f61bb7c7dc0ed459aae ), y: uint256( - 0x04ee544edd280fa185c8d9886c9e19832ef71d67fab5093eadeec49ccc5a34b1 + 0x1163822d778b1f374f971a3bbbac861c3b3b7eacdb0e506cf216c40921f12174 ) }), id3: Honk.G1Point({ x: uint256( - 0x11005f2ce25b6f954b89343fa059782ab8ecdc84d411c828cc7dfd54f796b6f6 + 0x211deee9dfac40c974eb8567e5f98f33c095c3b473646744ff9249c0141557f1 ), y: uint256( - 0x0c68694f3595aecaaffdff52c0fa5f1d05fa8c7ba651da1220634fa4b8867ba7 + 0x012032061151f693f8b9db5136efa15439a59fcef2ce2a994474cc0e38deea65 ) }), id4: Honk.G1Point({ x: uint256( - 0x1145c2f8face74c268cc68c772d99c59587659cbd594a1aef32a36ab556621e2 + 0x19feb056cf9e0242cd8b03b6382a7f4cc56ad7d791279cf4902bceaf76480661 ), y: uint256( - 0x1ffa60a61a074aa68c8412fd9f3b4ae61437cf3ff3f0497e1244ba56370d7b91 + 0x0ce22a0fb2bcfc2acceb26380d901ed53b28665d3202b66c178efe54ae0601e5 ) }), lagrangeFirst: Honk.G1Point({ @@ -237,10 +237,10 @@ library HonkVerificationKey { }), lagrangeLast: Honk.G1Point({ x: uint256( - 0x1f0071e7d3b9be4c8376ff230b3b8b242585d403d25463022d38e574cb0b5754 + 0x07a3911591931e1a78fe8e7034f651e03fa7512effe4eca4e5669efffccae5b3 ), y: uint256( - 0x19cb572dac34151573e3024878327a8ea7febb3d76c6171b10d1e3f2f2907584 + 0x1e175c0cba144505ad7bf362c1fa2edd1cec27db3efc62e7aaec8f974ebd5d3d ) }) }); @@ -3149,7 +3149,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract RecursiveAggregationFoldVerifier is +contract DecryptionAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { function loadVerificationKey() diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/ThresholdPkAggregationVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol similarity index 96% rename from packages/enclave-contracts/contracts/verifiers/bfv/honk/ThresholdPkAggregationVerifier.sol rename to packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index 39ddf0f3bf..c913812e71 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/ThresholdPkAggregationVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -5,10 +5,10 @@ // or FITNESS FOR A PARTICULAR PURPOSE. pragma solidity >=0.8.21; -uint256 constant N = 262144; -uint256 constant LOG_N = 18; -uint256 constant NUMBER_OF_PUBLIC_INPUTS = 20; -uint256 constant VK_HASH = 0x02475aeabcd71523e3bab9d9af5b42319b9c330cde7a59df61db5fdfcc341245; +uint256 constant N = 2097152; +uint256 constant LOG_N = 21; +uint256 constant NUMBER_OF_PUBLIC_INPUTS = 29; +uint256 constant VK_HASH = 0x07706ac4275caeba3a6e83631b7cc30dc8322839a88f5a941700619161f38391; library HonkVerificationKey { function loadVerificationKey() internal @@ -16,151 +16,151 @@ library HonkVerificationKey { returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ - circuitSize: uint256(262144), - logCircuitSize: uint256(18), - publicInputsSize: uint256(20), + circuitSize: uint256(2097152), + logCircuitSize: uint256(21), + publicInputsSize: uint256(29), ql: Honk.G1Point({ x: uint256( - 0x2d869a3344618ace9c9598845825574d0ca67162e128a50d90489ff3bfadc7dc + 0x111a728f6d97feb2d07c8650c58297f43fa1cb737536686ba611035833266766 ), y: uint256( - 0x0f121d185fb1616ed8afe0c67d7c7decb30bc3a8afcd2a9cf6fe597d8e111eab + 0x0fd591041aa17dcbf78787ba1f6eb644df43fb92c7b60870b3c29dd033933cd1 ) }), qr: Honk.G1Point({ x: uint256( - 0x2386fc6f436231094ea397e43d760c033cee1fff5b54904716beb9a15cd4cd78 + 0x062bd2526fa803f36afac0562659df1a9f4a0c6589aa65c6c6af7ed41d973839 ), y: uint256( - 0x0e5cee641089a8a4c1804f695a5a5da3eb432208259c892e85644d17e62a4bd9 + 0x0ea545fff69b5528031196319619d3bc836a0b18c4b0d8323c2c3f235f225c50 ) }), qo: Honk.G1Point({ x: uint256( - 0x1cad84fdbbce60a23b11b322697bfd54a2cdd5fd9b1204f1a9152a5aff931433 + 0x2f0724b72d5ed5a5fbe800439d3e9a5f0b202dbfacd761a0b4e4686fed34c6a1 ), y: uint256( - 0x0d830c5055422e01e2e408ce02346b1129631177c5bb0888ad38e8d294434f6a + 0x28bdb4ee0c725dfd07c5c7409c846bcd4ecaedfaa5806962add7336452afa15d ) }), q4: Honk.G1Point({ x: uint256( - 0x26ad16f77c5a3fad1b609a8071e176feaff03ae3300fde4b7da9f08bbd742bf0 + 0x0c39b0d165d345799d17d07f9950f4002d6e60382e9b56eb6c14010d467e6aba ), y: uint256( - 0x03b704cb7d0d70f6825a29e85bb42671ac10ef94ca4c686bc40465c4a3e611d8 + 0x2e49a73ee7e9bb5a90edc5e724beb2fd71ccf5fcaa5f30d7b86c290de9be1e90 ) }), qm: Honk.G1Point({ x: uint256( - 0x1eb2074702f2dd6b6dfdc1eab1e84152d8b61350a9bdba7e02a4debbf0f5459e + 0x1536bfcfa305be4288c24dedcd842d1700eea427c52b732d404e83a426babb68 ), y: uint256( - 0x097e1aaefb983e25c043b0f1f29a422915e164f67fcadfbefc48f49c8c8b9be5 + 0x21a70fbc5c35eb021d4e24055c2fcdd215d7711ce8d99c001c7327be43500162 ) }), qc: Honk.G1Point({ x: uint256( - 0x2447015b89854404d6c146c1715ff8628923eb6c1b5308b06ac7d7754704eebd + 0x0bd8b8c90ec9f0acab4b254d831db478a05a95e8ab4bb5d0cb9c7fd38ff4e837 ), y: uint256( - 0x2194d229e612fd76f5f216aba01fedcf434ac2d750d81ef5083003a488770aff + 0x152422e5e5f4c10b9d337cf5284a22c017afd562787f81cce3e573e948cbebde ) }), qLookup: Honk.G1Point({ x: uint256( - 0x038147f7985ab2a8d179a7a764ee622afc5cac5dabeb8f74a2f89ad33f068fe4 + 0x2ee537ee80c33fc3003e53f2fa67b14d63887059b541c3d5dbe38bfd517aa579 ), y: uint256( - 0x24095afc77887b20f8293c4a2602c55c7f68b34721dbdaa9d8f2cfc9f432da95 + 0x0ecddf7b61838886fcf1a7fec5c69d8e02933d1e153e33e5ea7d03b0c21b314a ) }), qArith: Honk.G1Point({ x: uint256( - 0x2df760548fec37cbaf3935d6edb2e3d84b4d3ef0828e722219f62d0da6d4bd9f + 0x2ae5c90804b7a2eee8676464c0afd8105452bc0b5160c5af08e07332f642509b ), y: uint256( - 0x11410040b7fcd060feeea7aff808cd7f6795d805ea220090627179ebe6a3811d + 0x1faa3c5b0d043233968e58ab03d1e3988d53933fd5716f73dfceaa283321fedc ) }), qDeltaRange: Honk.G1Point({ x: uint256( - 0x291e71689919516e94ed8e1e57c730c16aeed24dd31c3eb08d912171b25478ca + 0x12fabb263760a8bd891d6549cbbe45b5db26449b9095e982986ba5f0f8b71bcf ), y: uint256( - 0x08336ea9b282b13333a1133aab78c1ae83a5f4a8ccba3474b4a1c01c1d2df5b6 + 0x206f2d10317a3ba8c5147afb8c356eaa6af0912f3dd5f6af3acf4caac3075e6f ) }), qElliptic: Honk.G1Point({ x: uint256( - 0x2da351eb928c1184460bb00f8f39d7404b058ac5ccb9ee6fee4692c00e9d29e1 + 0x0791fed23dfada00ede6c9c02c9d8c5203db1c0aefb755b6bfd8858fd6ecbf20 ), y: uint256( - 0x273baed4efa3c0e468d3e92102daf28065c3ac73a123c2ede57513828522068e + 0x21b24edba4ae135a28e9d5537974b00b80b0e1eb42850f3f659af9e923887ec0 ) }), qMemory: Honk.G1Point({ x: uint256( - 0x1c07ca861823406afab7a6c9db5d73572c60fb1ccf3baad76c851cb30492e4c2 + 0x09e78ace9cfdcf5ad7cc6802eecf242bc9e3e198894af77aaf3debcc2bd32fa5 ), y: uint256( - 0x121fc5260fada2e8cb2c4abbf43f52acaf2ddcc831779e55d3cf6de207c1396e + 0x14fd3c6a7580ed1a7de952571906b5ce0185a46514fa57d0e04ab32c0ca0dd34 ) }), qNnf: Honk.G1Point({ x: uint256( - 0x0f002b28bee38725fd2ffe98719d1d1be8d94770561f8f145221592e3d602396 + 0x0fd61103ff82eb1bae7052f5497eab0a2faf991c16f7e69d15f07cae89cce2ba ), y: uint256( - 0x20c5817fa5abd7deb48227c7f5e9e60f059e83dec9d704e2f7263f4c9453a444 + 0x1e1dd49698f822a28bb1395b3f1f13f15e59d374ad9a55c90f32dd77ce17c858 ) }), qPoseidon2External: Honk.G1Point({ x: uint256( - 0x1c16a05dc078ef69a0ce524166febc25c3295b274672d68011410f97bbc82e5d + 0x2aec5550a01a45e0bc78f9c2c92eeab9dbb308125921dda7789fcff1d6d0db5d ), y: uint256( - 0x19ac5c0bc528b1fe4ac4ead4b32867534ad4a4d622a39349975aaaefe1eda456 + 0x117277f243642f35b0de763270a2f67f23c1a577fb9b645068f9bf3a8142f1bf ) }), qPoseidon2Internal: Honk.G1Point({ x: uint256( - 0x29c7a46c09346cee558000b76c2d04954d55eea1f852773213d5d46ae26811e1 + 0x231e7ffadf1a4fe4c731aadba30f4e688c57036f2f0a292f6f4ed598bf5c45be ), y: uint256( - 0x084b57a1a5189ace69a4f18d1a7207b510add93936450ceba38b2c569f8a5924 + 0x04ad4cd62afd2071e675fb9f806fd010bdde31728b54162c5d388ff8407311a9 ) }), s1: Honk.G1Point({ x: uint256( - 0x2a9b0bf63b502f894a26f6888ff70393651bf3b12609385afd6d1f06aca43767 + 0x12e76ee7f67d824af0f9eaf001d62ee30e3f77812e99060eb6f89c2cc0e2b825 ), y: uint256( - 0x227a7bcd3481c9497a5ac70200c7d5ce09009c3d9977f3c581172d540f570dc4 + 0x116e5a8a4011461fb9c38f402e6b1c6cc46bb827e701c4d8dc94f6e6e9b05085 ) }), s2: Honk.G1Point({ x: uint256( - 0x294532f305861aefc7cee7e2764556532c96f9fe6599c1198dcb93a92a3e1583 + 0x27474a8477926570b376c9484b946a50407ad175301d24f0823aa48bd4225990 ), y: uint256( - 0x04be125f7bd7d3e1f7e6dcfb0a982223fbc29e009079e94dc1617f1304327a98 + 0x0ac3513a35e7af6ee46189ca6e0a2d701f7568427a6f6cab64490652985904f1 ) }), s3: Honk.G1Point({ x: uint256( - 0x2ea11d664bd771919acfcb3fa4baeaf34cbe3e7415b557cf1ffddafa9eebbfd3 + 0x25446ef56d42b22185a7b772992264f8614dd81c830197979efa2ce70b81c94c ), y: uint256( - 0x177337343e6360249ac594749319d7fa768597e842fb41888b5fd8603c2c9837 + 0x1fc621751d8cc710f997f7665d71c5969433e4148f625915610d437f25771a9a ) }), s4: Honk.G1Point({ x: uint256( - 0x08551a059889ab05dc05edf3562931191874bd8411fd5930c19e35d0b5548e46 + 0x101488084a381a1a19fc537fff3cfdbc971c14810faf379184d40a10f8a2b111 ), y: uint256( - 0x11f157b2b08ea3b5c46897ff840aef3b624bc450d883b8e9aff3a05a4d80f2a3 + 0x222d5570c3352d841d289672c2150d62852bb77cae4798e22890d5772e678c9b ) }), t1: Honk.G1Point({ @@ -197,34 +197,34 @@ library HonkVerificationKey { }), id1: Honk.G1Point({ x: uint256( - 0x0ed0569ddf995d110f6ea9ddc0ad7ef2b89437f9c9b805343b098c3476f38ef9 + 0x2c9d9734c6b4280d3b45cd2f229b94ef9d6ea0cc4b12d3115772c8a44da778cd ), y: uint256( - 0x080465d26877747828e59a8d95b32bc291ca44246475579e07510cc9514b0d3f + 0x1bb5ebb400bf039d6bf710f9f260cbb912ee939afeef8d9d7038419872b51800 ) }), id2: Honk.G1Point({ x: uint256( - 0x21ad531b6a60ee3905164f5474c18e127f0b75d08d513e7f8b1766013fe48f80 + 0x03d6c8a21ed149f265d420e561a26db16aa036f6255b1d7cb1dbd7fef424e679 ), y: uint256( - 0x19a84735148cb361a618450c1d599e1b8b0cf6e49d16cc5c6f531090c4d81830 + 0x0bec696b07abc01e6d9be8ac176d7e3f22c1ba9745dfef9b8ec43544a6401b72 ) }), id3: Honk.G1Point({ x: uint256( - 0x1d8098fd996c0a465a5b0234a599802124fd0a8a27d2994aa80869ce393e2f2c + 0x08079b32015b38fd3892014f149d0ae2f96b1251d2614dd1b749d18996e9efd8 ), y: uint256( - 0x1d9e7330299673bd25d6c083d3f61b27f7814dd02cf65819eb081347f142e901 + 0x154c73548738dbb8b06e4a787dd140c0ff343b94223a1ed3af02851ae55ed685 ) }), id4: Honk.G1Point({ x: uint256( - 0x220285f610aa7a8b71275ddbdb87b7a3cd90c9c2a95c800a08354d7ac58a3ff2 + 0x036e02a212e0ea99da1fc3b9c88f80132bfce41225a39da38588bb8fb646dcc7 ), y: uint256( - 0x0d0e028920bb52b7988e842361c6111554abb86de333606a3b8f96304ee9b755 + 0x212442209a29191258fc995d08e625ec8c4cd47e930363a0398644cbb11c30c4 ) }), lagrangeFirst: Honk.G1Point({ @@ -237,10 +237,10 @@ library HonkVerificationKey { }), lagrangeLast: Honk.G1Point({ x: uint256( - 0x02d20232eecd70dae4eb288ff41e3506879cd365cd275c169b0d14c3ee61d2cb + 0x2dd29b2f19810390ba06ca70af2a664ae7bc0928bc4352eaf47ca52aecc163e8 ), y: uint256( - 0x1a1e682a381fe85231a85862daa051e44ebbe556741cec369a756320af977e7d + 0x2facd07b0816618d20a2dbeeb6a7f0045e981d6f0020135fc1131c8278a34730 ) }) }); @@ -3149,7 +3149,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract ThresholdPkAggregationVerifier is +contract DkgAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { function loadVerificationKey() diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/ThresholdDecryptedSharesAggregationVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/ThresholdDecryptedSharesAggregationVerifier.sol deleted file mode 100644 index b47b3c44f2..0000000000 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/ThresholdDecryptedSharesAggregationVerifier.sol +++ /dev/null @@ -1,3163 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. -pragma solidity >=0.8.21; - -uint256 constant N = 131072; -uint256 constant LOG_N = 17; -uint256 constant NUMBER_OF_PUBLIC_INPUTS = 120; -uint256 constant VK_HASH = 0x006492da92f08a975091a25b2388c15d5e31d556e1e736f68f049c17f508a0f7; -library HonkVerificationKey { - function loadVerificationKey() - internal - pure - returns (Honk.VerificationKey memory) - { - Honk.VerificationKey memory vk = Honk.VerificationKey({ - circuitSize: uint256(131072), - logCircuitSize: uint256(17), - publicInputsSize: uint256(120), - ql: Honk.G1Point({ - x: uint256( - 0x02a044fd06cd7b24942c2abaa59729eedc0a19a1bbccbd665b861997f4d8bccd - ), - y: uint256( - 0x0f787ca6a2c1ea64fee756dc4dc8f01ee1231b9cc77469b9174d3d8a7a363b25 - ) - }), - qr: Honk.G1Point({ - x: uint256( - 0x01b4b5146af758dcac2d33e6a7217c2df88a87c027c2bcdaebc70f12e3a4e833 - ), - y: uint256( - 0x075508943d6dde0268af6e8651139633111abea94eba052ed41273a37035fa11 - ) - }), - qo: Honk.G1Point({ - x: uint256( - 0x09b9b45ef104dfa3f39c0ea6ef5dd3a489940d62d366bcccf810cc954bff1796 - ), - y: uint256( - 0x2fd142546ffcd1dcf46c11d4ed597d15cc640981bffcb1dee838ff4df3a663f4 - ) - }), - q4: Honk.G1Point({ - x: uint256( - 0x1f41dd9a00f89c9acc02b51b114f22fe45a2ba2f27d2f922c082561b4beb8d09 - ), - y: uint256( - 0x2e93ef39c032fa2ca35199696adbb4131b8e8e107f298ffa674205b94605c7eb - ) - }), - qm: Honk.G1Point({ - x: uint256( - 0x0d4bcc607e37417bd723c4c7946da59b343a473fedef579a5c031cb96f3a53cd - ), - y: uint256( - 0x1c46afee30ceb85906fb1fc2106fc39c70ba36b4bb8bf1016a43db26c45909b8 - ) - }), - qc: Honk.G1Point({ - x: uint256( - 0x1582855842b2e599198915afbcb2b11a30a1d9eed53d756e9da7e59f2c8a13cd - ), - y: uint256( - 0x1056201ea7f9daa086c4ab1598653c25194e221f41ae1b6914c64be017add2b9 - ) - }), - qLookup: Honk.G1Point({ - x: uint256( - 0x0cbf626466e29547e383e6e44295524d22ce02930eac81322c8e95c5c83a5187 - ), - y: uint256( - 0x0080c602403a502c36662581d00edb43fbe5145a66c5c3d90f12fa31840f6c3e - ) - }), - qArith: Honk.G1Point({ - x: uint256( - 0x1b67f4c10591e39a73153cf155c6b841a9e9ffd4e4b13b97895a4c3bac95b17f - ), - y: uint256( - 0x008960215829d1bfdc8947cfff6facfc8f0f356f85ebeae103a15b7e81812b6f - ) - }), - qDeltaRange: Honk.G1Point({ - x: uint256( - 0x26c4901698a32b5ed849b748094e82ad5c73c5599ce6567107f831144ba79dd7 - ), - y: uint256( - 0x0b5750e772b022309585b82465a544871eb110b68d8138f1630c941f450ecf4d - ) - }), - qElliptic: Honk.G1Point({ - x: uint256( - 0x1684112019eec88d7085bc49d21271174564d223e1adf1fbcddebbb46e547e60 - ), - y: uint256( - 0x086fb3fafec7b58139c4243a99beb7d77fa75694a0d893a8d33008deec7ac5e5 - ) - }), - qMemory: Honk.G1Point({ - x: uint256( - 0x1617d3d9a582ebbdfab4436b6069011eb9f38e6856010ed6ae6969339b0af029 - ), - y: uint256( - 0x02a9d60cc9b6b7893239d040e344223b00d35e6f6a380f532b3846493c524d4d - ) - }), - qNnf: Honk.G1Point({ - x: uint256( - 0x1096c9ba549299d85900f014ad8c29bf26e0c9c679cb679488ece57142810946 - ), - y: uint256( - 0x0f466ba638fd0e1353c042f313282342cd337758a47ced02185c5f9e3bbf1ee7 - ) - }), - qPoseidon2External: Honk.G1Point({ - x: uint256( - 0x2e3aaefa6e2db144789c614228f1c8dad2d58a5e7a4113ac60e756fb2d2a0ecf - ), - y: uint256( - 0x1c4fa0ae44599c41aacbe7ebe0ffe537a09f5d77de4265e36b80ddca1d1cbde2 - ) - }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256( - 0x0e5ab15ae428b51a74b427beea70fbd92ed7a6fff51d86d682c31bf84a3a29ab - ), - y: uint256( - 0x269276e429daaac7a0b971dfb5713a672686d25d0eac044582430053729a8b7f - ) - }), - s1: Honk.G1Point({ - x: uint256( - 0x011552d2dd0839f3d111063c39d9c027f96c1feec00a289e2c531370f096f5e6 - ), - y: uint256( - 0x0d006c5f5efecda241a2b6ad0905464e3b737e4b7864d705580ad1a2fb1a951d - ) - }), - s2: Honk.G1Point({ - x: uint256( - 0x0fc787c7e8b1db8a4de620d118b27f9b2446edb6a19f2c191cb6ca7c7566a69f - ), - y: uint256( - 0x221994346dd3d27023d4d6def6ee88cca468d528b899dc5f68d9765e7afb5025 - ) - }), - s3: Honk.G1Point({ - x: uint256( - 0x209aedd2d2221460809d33ed32f75b7684a2a99bdc74f0a870097ed425113fbd - ), - y: uint256( - 0x1d467cd1d4467cdf573dfa470446e80a532ae7cbf683acf5237c8e1dd2bbe285 - ) - }), - s4: Honk.G1Point({ - x: uint256( - 0x23cc02e2498302509707a7a966bf00fa2b8f3d98285b86aac3bfe432e25413c8 - ), - y: uint256( - 0x0a4d516c9472521470e62312e708ac52b9cebc659c7ddcddcb5df4aef76c6c2a - ) - }), - t1: Honk.G1Point({ - x: uint256( - 0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26 - ), - y: uint256( - 0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f - ) - }), - t2: Honk.G1Point({ - x: uint256( - 0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e - ), - y: uint256( - 0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19 - ) - }), - t3: Honk.G1Point({ - x: uint256( - 0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d - ), - y: uint256( - 0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e - ) - }), - t4: Honk.G1Point({ - x: uint256( - 0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce - ), - y: uint256( - 0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854 - ) - }), - id1: Honk.G1Point({ - x: uint256( - 0x0a330b234cf6f85c0049c6dae60a5180feeb36b38ea2c03fb041d35b0ff36638 - ), - y: uint256( - 0x2bfbe90e706022c4a237201b5900132d59ac44fbf596de26a9f3526469dbefe3 - ) - }), - id2: Honk.G1Point({ - x: uint256( - 0x1254120586683a7e4e1a2cb9868c95571a9c47c45cf48a312af87c266195afa6 - ), - y: uint256( - 0x138ae89c8588f106e91e276535a2a982e4bd9e12c400d3c934196d6a5e2e7940 - ) - }), - id3: Honk.G1Point({ - x: uint256( - 0x10b4638497ffe24f8faccee7a2dc2642c20d38c17ccbce308e180b199454d037 - ), - y: uint256( - 0x1742fd41969249f0c6636d55ac31d89e35b35f35dcaa5836104925c6cadfddda - ) - }), - id4: Honk.G1Point({ - x: uint256( - 0x049b651cbe75950126545589c99b8410ed210a79292f36cb99169b044acfb944 - ), - y: uint256( - 0x11b185db4578b47ac98385f614ceb4d6a0ea46d07455fd6e788ebd1d19120851 - ) - }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) - }), - lagrangeLast: Honk.G1Point({ - x: uint256( - 0x1b0cd706de7e1b2455f6ae8f4ca69747e4a4b9362260fbad8c623ad8f9a4d5dc - ), - y: uint256( - 0x20446017b99d830cf390875d57dff60403843e423f9f573268049e34c4f90741 - ) - }) - }); - return vk; - } -} - -pragma solidity ^0.8.27; - -interface IVerifier { - function verify( - bytes calldata _proof, - bytes32[] calldata _publicInputs - ) external returns (bool); -} - -type Fr is uint256; - -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; - -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; - -uint256 constant SUBGROUP_SIZE = 256; -uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order -uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap( - 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 -); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( - 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 -); -Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); -Fr constant ONE = Fr.wrap(1); -Fr constant ZERO = Fr.wrap(0); -// Instantiation - -library FrLib { - function from(uint256 value) internal pure returns (Fr) { - unchecked { - return Fr.wrap(value % MODULUS); - } - } - - function fromBytes32(bytes32 value) internal pure returns (Fr) { - unchecked { - return Fr.wrap(uint256(value) % MODULUS); - } - } - - function toBytes32(Fr value) internal pure returns (bytes32) { - unchecked { - return bytes32(Fr.unwrap(value)); - } - } - - function invert(Fr value) internal view returns (Fr) { - uint256 v = Fr.unwrap(value); - uint256 result; - - // Call the modexp precompile to invert in the field - assembly { - let free := mload(0x40) - mstore(free, 0x20) - mstore(add(free, 0x20), 0x20) - mstore(add(free, 0x40), 0x20) - mstore(add(free, 0x60), v) - mstore(add(free, 0x80), sub(MODULUS, 2)) - mstore(add(free, 0xa0), MODULUS) - let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) - if iszero(success) { - revert(0, 0) - } - result := mload(0x00) - mstore(0x40, add(free, 0x80)) - } - - return Fr.wrap(result); - } - - function pow(Fr base, uint256 v) internal view returns (Fr) { - uint256 b = Fr.unwrap(base); - uint256 result; - - // Call the modexp precompile to invert in the field - assembly { - let free := mload(0x40) - mstore(free, 0x20) - mstore(add(free, 0x20), 0x20) - mstore(add(free, 0x40), 0x20) - mstore(add(free, 0x60), b) - mstore(add(free, 0x80), v) - mstore(add(free, 0xa0), MODULUS) - let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) - if iszero(success) { - revert(0, 0) - } - result := mload(0x00) - mstore(0x40, add(free, 0x80)) - } - - return Fr.wrap(result); - } - - function div(Fr numerator, Fr denominator) internal view returns (Fr) { - unchecked { - return numerator * invert(denominator); - } - } - - function sqr(Fr value) internal pure returns (Fr) { - unchecked { - return value * value; - } - } - - function unwrap(Fr value) internal pure returns (uint256) { - unchecked { - return Fr.unwrap(value); - } - } - - function neg(Fr value) internal pure returns (Fr) { - unchecked { - return Fr.wrap(MODULUS - Fr.unwrap(value)); - } - } -} - -// Free functions -function add(Fr a, Fr b) pure returns (Fr) { - unchecked { - return Fr.wrap(addmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); - } -} - -function mul(Fr a, Fr b) pure returns (Fr) { - unchecked { - return Fr.wrap(mulmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); - } -} - -function sub(Fr a, Fr b) pure returns (Fr) { - unchecked { - return Fr.wrap(addmod(Fr.unwrap(a), MODULUS - Fr.unwrap(b), MODULUS)); - } -} - -function exp(Fr base, Fr exponent) pure returns (Fr) { - if (Fr.unwrap(exponent) == 0) return Fr.wrap(1); - // Implement exponent with a loop as we will overflow otherwise - for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) { - base = base * base; - } - return base; -} - -function notEqual(Fr a, Fr b) pure returns (bool) { - unchecked { - return Fr.unwrap(a) != Fr.unwrap(b); - } -} - -function equal(Fr a, Fr b) pure returns (bool) { - unchecked { - return Fr.unwrap(a) == Fr.unwrap(b); - } -} - -uint256 constant CONST_PROOF_SIZE_LOG_N = 28; - -uint256 constant NUMBER_OF_SUBRELATIONS = 28; -uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8; -uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; -uint256 constant NUMBER_OF_ENTITIES = 41; -// The number of entities added for ZK (gemini_masking_poly) -uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + - NUM_MASKING_POLYNOMIALS; -uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + - NUM_MASKING_POLYNOMIALS; -uint256 constant NUMBER_TO_BE_SHIFTED = 5; -uint256 constant PAIRING_POINTS_SIZE = 16; - -uint256 constant FIELD_ELEMENT_SIZE = 0x20; -uint256 constant GROUP_ELEMENT_SIZE = 0x40; - -// Powers of alpha used to batch subrelations (alpha, alpha^2, ..., alpha^(NUM_SUBRELATIONS-1)) -uint256 constant NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1; - -// ENUM FOR WIRES -enum WIRE { - Q_M, - Q_C, - Q_L, - Q_R, - Q_O, - Q_4, - Q_LOOKUP, - Q_ARITH, - Q_RANGE, - Q_ELLIPTIC, - Q_MEMORY, - Q_NNF, - Q_POSEIDON2_EXTERNAL, - Q_POSEIDON2_INTERNAL, - SIGMA_1, - SIGMA_2, - SIGMA_3, - SIGMA_4, - ID_1, - ID_2, - ID_3, - ID_4, - TABLE_1, - TABLE_2, - TABLE_3, - TABLE_4, - LAGRANGE_FIRST, - LAGRANGE_LAST, - W_L, - W_R, - W_O, - W_4, - Z_PERM, - LOOKUP_INVERSES, - LOOKUP_READ_COUNTS, - LOOKUP_READ_TAGS, - W_L_SHIFT, - W_R_SHIFT, - W_O_SHIFT, - W_4_SHIFT, - Z_PERM_SHIFT -} - -library Honk { - struct G1Point { - uint256 x; - uint256 y; - } - - struct VerificationKey { - // Misc Params - uint256 circuitSize; - uint256 logCircuitSize; - uint256 publicInputsSize; - // Selectors - G1Point qm; - G1Point qc; - G1Point ql; - G1Point qr; - G1Point qo; - G1Point q4; - G1Point qLookup; // Lookup - G1Point qArith; // Arithmetic widget - G1Point qDeltaRange; // Delta Range sort - G1Point qMemory; // Memory - G1Point qNnf; // Non-native Field - G1Point qElliptic; // Auxillary - G1Point qPoseidon2External; - G1Point qPoseidon2Internal; - // Copy constraints - G1Point s1; - G1Point s2; - G1Point s3; - G1Point s4; - // Copy identity - G1Point id1; - G1Point id2; - G1Point id3; - G1Point id4; - // Precomputed lookup table - G1Point t1; - G1Point t2; - G1Point t3; - G1Point t4; - // Fixed first and last - G1Point lagrangeFirst; - G1Point lagrangeLast; - } - - struct RelationParameters { - // challenges - Fr eta; - Fr etaTwo; - Fr etaThree; - Fr beta; - Fr gamma; - // derived - Fr publicInputsDelta; - } - - struct Proof { - // Pairing point object - Fr[PAIRING_POINTS_SIZE] pairingPointObject; - // Free wires - G1Point w1; - G1Point w2; - G1Point w3; - G1Point w4; - // Lookup helpers - Permutations - G1Point zPerm; - // Lookup helpers - logup - G1Point lookupReadCounts; - G1Point lookupReadTags; - G1Point lookupInverses; - // Sumcheck - Fr[BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; - Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations; - // Shplemini - G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; - Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; - G1Point shplonkQ; - G1Point kzgQuotient; - } - - /// forge-lint: disable-next-item(pascal-case-struct) - struct ZKProof { - // Pairing point object - Fr[PAIRING_POINTS_SIZE] pairingPointObject; - // ZK: Gemini masking polynomial commitment (sent first, right after public inputs) - G1Point geminiMaskingPoly; - // Commitments to wire polynomials - G1Point w1; - G1Point w2; - G1Point w3; - G1Point w4; - // Commitments to logup witness polynomials - G1Point lookupReadCounts; - G1Point lookupReadTags; - G1Point lookupInverses; - // Commitment to grand permutation polynomial - G1Point zPerm; - G1Point[3] libraCommitments; - // Sumcheck - Fr libraSum; - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; - Fr libraEvaluation; - Fr[NUMBER_OF_ENTITIES_ZK] sumcheckEvaluations; // Includes gemini_masking_poly eval at index 0 (first position) - // Shplemini - G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; - Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; - Fr[4] libraPolyEvals; - G1Point shplonkQ; - G1Point kzgQuotient; - } -} - -// ZKTranscript library to generate fiat shamir challenges, the ZK transcript only differest -/// forge-lint: disable-next-item(pascal-case-struct) -struct ZKTranscript { - // Oink - Honk.RelationParameters relationParameters; - Fr[NUMBER_OF_ALPHAS] alphas; // Powers of alpha: [alpha, alpha^2, ..., alpha^(NUM_SUBRELATIONS-1)] - Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; - // Sumcheck - Fr libraChallenge; - Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; - // Shplemini - Fr rho; - Fr geminiR; - Fr shplonkNu; - Fr shplonkZ; - // Derived - Fr publicInputsDelta; -} - -library ZKTranscriptLib { - function generateTranscript( - Honk.ZKProof memory proof, - bytes32[] calldata publicInputs, - uint256 vkHash, - uint256 publicInputsSize, - uint256 logN - ) external pure returns (ZKTranscript memory t) { - Fr previousChallenge; - ( - t.relationParameters, - previousChallenge - ) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); - - (t.alphas, previousChallenge) = generateAlphaChallenges( - previousChallenge, - proof - ); - - (t.gateChallenges, previousChallenge) = generateGateChallenges( - previousChallenge, - logN - ); - (t.libraChallenge, previousChallenge) = generateLibraChallenge( - previousChallenge, - proof - ); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( - proof, - previousChallenge, - logN - ); - - (t.rho, previousChallenge) = generateRhoChallenge( - proof, - previousChallenge - ); - - (t.geminiR, previousChallenge) = generateGeminiRChallenge( - proof, - previousChallenge, - logN - ); - - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( - proof, - previousChallenge, - logN - ); - - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( - proof, - previousChallenge - ); - return t; - } - - function splitChallenge( - Fr challenge - ) internal pure returns (Fr first, Fr second) { - uint256 challengeU256 = uint256(Fr.unwrap(challenge)); - // Split into two equal 127-bit chunks (254/2) - uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits - uint256 hi = challengeU256 >> 127; - first = FrLib.fromBytes32(bytes32(lo)); - second = FrLib.fromBytes32(bytes32(hi)); - } - - function generateRelationParametersChallenges( - Honk.ZKProof memory proof, - bytes32[] calldata publicInputs, - uint256 vkHash, - uint256 publicInputsSize, - Fr previousChallenge - ) - internal - pure - returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) - { - ( - rp.eta, - rp.etaTwo, - rp.etaThree, - previousChallenge - ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - - ( - rp.beta, - rp.gamma, - nextPreviousChallenge - ) = generateBetaAndGammaChallenges(previousChallenge, proof); - } - - function generateEtaChallenge( - Honk.ZKProof memory proof, - bytes32[] calldata publicInputs, - uint256 vkHash, - uint256 publicInputsSize - ) - internal - pure - returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) - { - // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) - bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); - round0[0] = bytes32(vkHash); - - for (uint256 i = 0; i < publicInputsSize - PAIRING_POINTS_SIZE; i++) { - round0[1 + i] = bytes32(publicInputs[i]); - } - for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib - .toBytes32(proof.pairingPointObject[i]); - } - - // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) - round0[1 + publicInputsSize] = bytes32(proof.geminiMaskingPoly.x); - round0[1 + publicInputsSize + 1] = bytes32(proof.geminiMaskingPoly.y); - - // Create the first challenge - // Note: w4 is added to the challenge later on - round0[1 + publicInputsSize + 2] = bytes32(proof.w1.x); - round0[1 + publicInputsSize + 3] = bytes32(proof.w1.y); - round0[1 + publicInputsSize + 4] = bytes32(proof.w2.x); - round0[1 + publicInputsSize + 5] = bytes32(proof.w2.y); - round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); - round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round0)) - ); - (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - - (etaThree, ) = splitChallenge(previousChallenge); - } - - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { - bytes32[7] memory round1; - round1[0] = FrLib.toBytes32(previousChallenge); - round1[1] = bytes32(proof.lookupReadCounts.x); - round1[2] = bytes32(proof.lookupReadCounts.y); - round1[3] = bytes32(proof.lookupReadTags.x); - round1[4] = bytes32(proof.lookupReadTags.y); - round1[5] = bytes32(proof.w4.x); - round1[6] = bytes32(proof.w4.y); - - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round1)) - ); - (beta, gamma) = splitChallenge(nextPreviousChallenge); - } - - // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) - internal - pure - returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) - { - // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup - uint256[5] memory alpha0; - alpha0[0] = Fr.unwrap(previousChallenge); - alpha0[1] = proof.lookupInverses.x; - alpha0[2] = proof.lookupInverses.y; - alpha0[3] = proof.zPerm.x; - alpha0[4] = proof.zPerm.y; - - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(alpha0)) - ); - Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); - - // Compute powers of alpha for batching subrelations - alphas[0] = alpha; - for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) { - alphas[i] = alphas[i - 1] * alpha; - } - } - - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) - internal - pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, - Fr nextPreviousChallenge - ) - { - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - (gateChallenges[0], ) = splitChallenge(previousChallenge); - for (uint256 i = 1; i < logN; i++) { - gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; - } - nextPreviousChallenge = previousChallenge; - } - - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { - // 2 comm, 1 sum, 1 challenge - uint256[4] memory challengeData; - challengeData[0] = Fr.unwrap(previousChallenge); - challengeData[1] = proof.libraCommitments[0].x; - challengeData[2] = proof.libraCommitments[0].y; - challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(challengeData)) - ); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); - } - - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) - internal - pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, - Fr nextPreviousChallenge - ) - { - for (uint256 i = 0; i < logN; i++) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; - univariateChal[0] = prevChallenge; - - for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; - } - prevChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(univariateChal)) - ); - - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); - } - nextPreviousChallenge = prevChallenge; - } - - // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { - uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; - rhoChallengeElements[0] = Fr.unwrap(prevChallenge); - uint256 i; - for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap( - proof.sumcheckEvaluations[i - 1] - ); - } - rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); - i += 1; - rhoChallengeElements[i] = proof.libraCommitments[1].x; - rhoChallengeElements[i + 1] = proof.libraCommitments[1].y; - i += 2; - rhoChallengeElements[i] = proof.libraCommitments[2].x; - rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(rhoChallengeElements)) - ); - (rho, ) = splitChallenge(nextPreviousChallenge); - } - - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { - uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); - gR[0] = Fr.unwrap(prevChallenge); - - for (uint256 i = 0; i < logN - 1; i++) { - gR[1 + i * 2] = proof.geminiFoldComms[i].x; - gR[2 + i * 2] = proof.geminiFoldComms[i].y; - } - - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(gR)) - ); - - (geminiR, ) = splitChallenge(nextPreviousChallenge); - } - - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[]( - logN + 1 + 4 - ); - shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); - - for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.geminiAEvaluations[i - 1] - ); - } - - uint256 libraIdx = 0; - for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.libraPolyEvals[libraIdx] - ); - libraIdx++; - } - - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkNuChallengeElements)) - ); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); - } - - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { - uint256[3] memory shplonkZChallengeElements; - shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); - - shplonkZChallengeElements[1] = proof.shplonkQ.x; - shplonkZChallengeElements[2] = proof.shplonkQ.y; - - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkZChallengeElements)) - ); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); - } - - function loadProof( - bytes calldata proof, - uint256 logN - ) internal pure returns (Honk.ZKProof memory p) { - uint256 boundary = 0x0; - - // Pairing point object - for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); - boundary += FIELD_ELEMENT_SIZE; - } - - // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); - boundary += GROUP_ELEMENT_SIZE; - - // Commitments - p.w1 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.w2 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.w3 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - - // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); - boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); - boundary += GROUP_ELEMENT_SIZE; - p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); - boundary += GROUP_ELEMENT_SIZE; - p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); - boundary += GROUP_ELEMENT_SIZE; - - p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); - boundary += FIELD_ELEMENT_SIZE; - // Sumcheck univariates - for (uint256 i = 0; i < logN; i++) { - for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); - boundary += FIELD_ELEMENT_SIZE; - } - } - - // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) - for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); - boundary += FIELD_ELEMENT_SIZE; - } - - p.libraEvaluation = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); - boundary += FIELD_ELEMENT_SIZE; - - p.libraCommitments[1] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); - boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); - boundary += GROUP_ELEMENT_SIZE; - - // Gemini - // Read gemini fold univariates - for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); - boundary += GROUP_ELEMENT_SIZE; - } - - // Read gemini a evaluations - for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); - boundary += FIELD_ELEMENT_SIZE; - } - - for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); - boundary += FIELD_ELEMENT_SIZE; - } - - // Shplonk - p.shplonkQ = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); - boundary += GROUP_ELEMENT_SIZE; - // KZG - p.kzgQuotient = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); - } -} - -// Field arithmetic libraries - -library RelationsLib { - Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17) - - function accumulateRelationEvaluations( - Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations, - Honk.RelationParameters memory rp, - Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges, - Fr powPartialEval - ) internal pure returns (Fr accumulator) { - Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; - - // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePermutationRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateLogDerivativeLookupRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateDeltaRangeRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateEllipticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateMemoryRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateNnfRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonExternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonInternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - - // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations( - evaluations, - subrelationChallenges - ); - } - - /** - * Aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids - * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code - * editors, and thus is noisy. - */ - function wire( - Fr[NUMBER_OF_ENTITIES] memory p, - WIRE _wire - ) internal pure returns (Fr) { - return p[uint256(_wire)]; - } - - uint256 internal constant NEG_HALF_MODULO_P = - 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; - /** - * Ultra Arithmetic Relation - * - */ - - function accumulateArithmeticRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - // Relation 0 - Fr q_arith = wire(p, WIRE.Q_ARITH); - { - Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P); - - Fr accum = (q_arith - Fr.wrap(3)) * - (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * - neg_half; - accum = - accum + - (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + - (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + - (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + - wire(p, WIRE.Q_C); - accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); - accum = accum * q_arith; - accum = accum * domainSep; - evals[0] = accum; - } - - // Relation 1 - { - Fr accum = wire(p, WIRE.W_L) + - wire(p, WIRE.W_4) - - wire(p, WIRE.W_L_SHIFT) + - wire(p, WIRE.Q_M); - accum = accum * (q_arith - Fr.wrap(2)); - accum = accum * (q_arith - ONE); - accum = accum * q_arith; - accum = accum * domainSep; - evals[1] = accum; - } - } - - function accumulatePermutationRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Honk.RelationParameters memory rp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - Fr grand_product_numerator; - Fr grand_product_denominator; - - { - Fr num = wire(p, WIRE.W_L) + - wire(p, WIRE.ID_1) * - rp.beta + - rp.gamma; - num = - num * - (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); - - grand_product_numerator = num; - } - { - Fr den = wire(p, WIRE.W_L) + - wire(p, WIRE.SIGMA_1) * - rp.beta + - rp.gamma; - den = - den * - (wire(p, WIRE.W_R) + - wire(p, WIRE.SIGMA_2) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_O) + - wire(p, WIRE.SIGMA_3) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_4) + - wire(p, WIRE.SIGMA_4) * - rp.beta + - rp.gamma); - - grand_product_denominator = den; - } - - // Contribution 2 - { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * - grand_product_numerator; - - acc = - acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + - (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * - grand_product_denominator); - acc = acc * domainSep; - evals[2] = acc; - } - - // Contribution 3 - { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * - wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; - evals[3] = acc; - } - } - - function accumulateLogDerivativeLookupRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Honk.RelationParameters memory rp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - Fr write_term; - Fr read_term; - - // Calculate the write term (the table accumulation) - { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); - } - - // Calculate the write term - { - Fr derived_entry_1 = wire(p, WIRE.W_L) + - rp.gamma + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + - wire(p, WIRE.Q_M) * - wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + - wire(p, WIRE.Q_C) * - wire(p, WIRE.W_O_SHIFT); - - read_term = - derived_entry_1 + - (derived_entry_2 * rp.eta) + - (derived_entry_3 * rp.etaTwo) + - (wire(p, WIRE.Q_O) * rp.etaThree); - } - - Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; - Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); - - // Inverse calculated correctly relation - Fr accumulatorNone = read_term * - write_term * - wire(p, WIRE.LOOKUP_INVERSES) - - inverse_exists_xor; - accumulatorNone = accumulatorNone * domainSep; - - // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * - read_inverse - - wire(p, WIRE.LOOKUP_READ_COUNTS) * - write_inverse; - - Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); - - Fr read_tag_boolean_relation = read_tag * read_tag - read_tag; - - evals[4] = accumulatorNone; - evals[5] = accumulatorOne; - evals[6] = read_tag_boolean_relation * domainSep; - } - - function accumulateDeltaRangeRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - Fr minus_one = ZERO - ONE; - Fr minus_two = ZERO - Fr.wrap(2); - Fr minus_three = ZERO - Fr.wrap(3); - - // Compute wire differences - Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L); - Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R); - Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O); - Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4); - - // Contribution 6 - { - Fr acc = delta_1; - acc = acc * (delta_1 + minus_one); - acc = acc * (delta_1 + minus_two); - acc = acc * (delta_1 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[7] = acc; - } - - // Contribution 7 - { - Fr acc = delta_2; - acc = acc * (delta_2 + minus_one); - acc = acc * (delta_2 + minus_two); - acc = acc * (delta_2 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[8] = acc; - } - - // Contribution 8 - { - Fr acc = delta_3; - acc = acc * (delta_3 + minus_one); - acc = acc * (delta_3 + minus_two); - acc = acc * (delta_3 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[9] = acc; - } - - // Contribution 9 - { - Fr acc = delta_4; - acc = acc * (delta_4 + minus_one); - acc = acc * (delta_4 + minus_two); - acc = acc * (delta_4 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[10] = acc; - } - } - - struct EllipticParams { - // Points - Fr x_1; - Fr y_1; - Fr x_2; - Fr y_2; - Fr y_3; - Fr x_3; - // push accumulators into memory - Fr x_double_identity; - } - - function accumulateEllipticRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - EllipticParams memory ep; - ep.x_1 = wire(p, WIRE.W_R); - ep.y_1 = wire(p, WIRE.W_O); - - ep.x_2 = wire(p, WIRE.W_L_SHIFT); - ep.y_2 = wire(p, WIRE.W_4_SHIFT); - ep.y_3 = wire(p, WIRE.W_O_SHIFT); - ep.x_3 = wire(p, WIRE.W_R_SHIFT); - - Fr q_sign = wire(p, WIRE.Q_L); - Fr q_is_double = wire(p, WIRE.Q_M); - - // Contribution 10 point addition, x-coordinate check - // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 - Fr x_diff = (ep.x_2 - ep.x_1); - Fr y1_sqr = (ep.y_1 * ep.y_1); - { - // Move to top - Fr partialEval = domainSep; - - Fr y2_sqr = (ep.y_2 * ep.y_2); - Fr y1y2 = ep.y_1 * ep.y_2 * q_sign; - Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1); - x_add_identity = x_add_identity * x_diff * x_diff; - x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - - evals[11] = - x_add_identity * - partialEval * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); - } - - // Contribution 11 point addition, x-coordinate check - // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 - { - Fr y1_plus_y3 = ep.y_1 + ep.y_3; - Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * - x_diff + - (ep.x_3 - ep.x_1) * - y_diff; - evals[12] = - y_add_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); - } - - // Contribution 10 point doubling, x-coordinate check - // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 - // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 - { - Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1; - Fr y1_sqr_mul_4 = y1_sqr + y1_sqr; - y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4; - Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); - - // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = - (ep.x_3 + ep.x_1 + ep.x_1) * - y1_sqr_mul_4 - - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; - evals[11] = evals[11] + acc; - } - - // Contribution 11 point doubling, y-coordinate check - // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 - { - Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * - (ep.x_1 - ep.x_3) - - (ep.y_1 + ep.y_1) * - (ep.y_1 + ep.y_3); - evals[12] = - evals[12] + - y_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; - } - } - - // Parameters used within the Memory Relation - // A struct is used to work around stack too deep. This relation has alot of variables - struct MemParams { - Fr memory_record_check; - Fr partial_record_check; - Fr next_gate_access_type; - Fr record_delta; - Fr index_delta; - Fr adjacent_values_match_if_adjacent_indices_match; - Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; - Fr access_check; - Fr next_gate_access_type_is_boolean; - Fr ROM_consistency_check_identity; - Fr RAM_consistency_check_identity; - Fr timestamp_delta; - Fr RAM_timestamp_check_identity; - Fr memory_identity; - Fr index_is_monotonically_increasing; - } - - function accumulateMemoryRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Honk.RelationParameters memory rp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - MemParams memory ap; - - /** - * MEMORY - * - * A RAM memory record contains a tuple of the following fields: - * * i: `index` of memory cell being accessed - * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) - * * v: `value` of memory cell being accessed - * * a: `access` type of record. read: 0 = read, 1 = write - * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three - * - * A ROM memory record contains a tuple of the following fields: - * * i: `index` of memory cell being accessed - * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) - * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) - * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three - * - * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + - * selectors, depending on whether the gate is a RAM read/write or a ROM read - * - * | gate type | i | v2/t | v | a | r | - * | --------- | -- | ----- | -- | -- | -- | - * | ROM | w1 | w2 | w3 | -- | w4 | - * | RAM | w1 | w2 | w3 | qc | w4 | - * - * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on - * `w2` to fix its value) - * - * - */ - - /** - * Memory Record Check - * Partial degree: 1 - * Total degree: 4 - * - * A ROM/ROM access gate can be evaluated with the identity: - * - * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0 - * - * For ROM gates, qc = 0 - */ - ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_L) * rp.eta); - ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); - ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 - ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); - - /** - * Contribution 13 & 14 - * ROM Consistency Check - * Partial degree: 1 - * Total degree: 4 - * - * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of - * records that are sorted. - * - * We apply the following checks for the sorted records: - * - * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 - * 2. index values for adjacent records are monotonically increasing - * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} - * - */ - ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); - ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - - ap.index_is_monotonically_increasing = - ap.index_delta * - (ap.index_delta - Fr.wrap(1)); // deg 2 - - ap.adjacent_values_match_if_adjacent_indices_match = - (ap.index_delta * MINUS_ONE + ONE) * - ap.record_delta; // deg 2 - - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - - ap.ROM_consistency_check_identity = - ap.memory_record_check * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 - - /** - * Contributions 15,16,17 - * RAM Consistency Check - * - * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check` - * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. - * This is validated by requiring `access` to be boolean - * - * For two adjacent entries in the sorted list if _both_ - * A) index values match - * B) adjacent access value is 0 (i.e. next gate is a READ) - * then - * C) both values must match. - * The gate boolean check is - * (A && B) => C === !(A && B) || C === !A || !B || C - * - * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized - * with a WRITE operation. - */ - Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4 - ap.access_check = access_type * (access_type - Fr.wrap(1)); // check value is 0 or 1; deg 2 or 8 - - // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta - // deg 1 or 4 - ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = - wire(p, WIRE.W_4_SHIFT) - - ap.next_gate_access_type; - - Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 - - // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the - // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't - // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access - // type is correct, to cover this edge case - // deg 2 or 4 - ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * - ap.next_gate_access_type - - ap.next_gate_access_type; - - // Putting it all together... - evals[16] = - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = - ap.next_gate_access_type_is_boolean * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = - ap.access_check * - (wire(p, WIRE.Q_O)); // deg 3 or 9 - - /** - * RAM Timestamp Consistency Check - * - * | w1 | w2 | w3 | w4 | - * | index | timestamp | timestamp_check | -- | - * - * Let delta_index = index_{i + 1} - index_{i} - * - * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i - * Else timestamp_check = 0 - */ - ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + ONE) * - ap.timestamp_delta - - wire(p, WIRE.W_O); // deg 3 - - /** - * Complete Contribution 12 - * The complete RAM/ROM memory identity - * Partial degree: - */ - ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6 - ap.memory_identity = - ap.memory_identity + - ap.RAM_timestamp_check_identity * - (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 - ap.memory_identity = - ap.memory_identity + - ap.memory_record_check * - (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 - ap.memory_identity = - ap.memory_identity + - ap.RAM_consistency_check_identity; // deg 3 or 9 - - // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = - ap.memory_identity * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 - evals[13] = ap.memory_identity; - } - - // Constants for the Non-native Field relation - Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); - Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); - - // Parameters used within the Non-Native Field Relation - // A struct is used to work around stack too deep. This relation has alot of variables - struct NnfParams { - Fr limb_subproduct; - Fr non_native_field_gate_1; - Fr non_native_field_gate_2; - Fr non_native_field_gate_3; - Fr limb_accumulator_1; - Fr limb_accumulator_2; - Fr nnf_identity; - } - - function accumulateNnfRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - NnfParams memory ap; - - /** - * Contribution 12 - * Non native field arithmetic gate 2 - * deg 4 - * - * _ _ - * / _ _ _ 14 \ - * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | - * \_ _/ - * - * - */ - ap.limb_subproduct = - wire(p, WIRE.W_L) * - wire(p, WIRE.W_R_SHIFT) + - wire(p, WIRE.W_L_SHIFT) * - wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * - wire(p, WIRE.W_4) + - wire(p, WIRE.W_R) * - wire(p, WIRE.W_O) - - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 - - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 + - ap.limb_subproduct; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 * - wire(p, WIRE.Q_4); - - ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = - ap.limb_subproduct + - (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); - ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 - - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 * - wire(p, WIRE.Q_O); - - ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 + - wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 - - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 * - wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + - ap.non_native_field_gate_2 + - ap.non_native_field_gate_3; - non_native_field_identity = - non_native_field_identity * - wire(p, WIRE.Q_R); - - // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm - // deg 2 - ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L); - ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4); - - // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm - // deg 2 - ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O); - ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - - Fr limb_accumulator_identity = ap.limb_accumulator_1 + - ap.limb_accumulator_2; - limb_accumulator_identity = - limb_accumulator_identity * - wire(p, WIRE.Q_O); // deg 3 - - ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; - ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); - evals[19] = ap.nnf_identity; - } - - struct PoseidonExternalParams { - Fr s1; - Fr s2; - Fr s3; - Fr s4; - Fr u1; - Fr u2; - Fr u3; - Fr u4; - Fr t0; - Fr t1; - Fr t2; - Fr t3; - Fr v1; - Fr v2; - Fr v3; - Fr v4; - Fr q_pos_by_scaling; - } - - function accumulatePoseidonExternalRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - PoseidonExternalParams memory ep; - - ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); - ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R); - ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O); - ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4); - - ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1; - ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2; - ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3; - ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4; - // matrix mul v = M_E * u with 14 additions - ep.t0 = ep.u1 + ep.u2; // u_1 + u_2 - ep.t1 = ep.u3 + ep.u4; // u_3 + u_4 - ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2 - // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4 - ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4 - // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4 - ep.v4 = ep.t1 + ep.t1; - ep.v4 = ep.v4 + ep.v4 + ep.t3; - // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4 - ep.v2 = ep.t0 + ep.t0; - ep.v2 = ep.v2 + ep.v2 + ep.t2; - // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4 - ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4 - ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 - - ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = - evals[20] + - ep.q_pos_by_scaling * - (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - - evals[21] = - evals[21] + - ep.q_pos_by_scaling * - (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - - evals[22] = - evals[22] + - ep.q_pos_by_scaling * - (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - - evals[23] = - evals[23] + - ep.q_pos_by_scaling * - (ep.v4 - wire(p, WIRE.W_4_SHIFT)); - } - - struct PoseidonInternalParams { - Fr u1; - Fr u2; - Fr u3; - Fr u4; - Fr u_sum; - Fr v1; - Fr v2; - Fr v3; - Fr v4; - Fr s1; - Fr q_pos_by_scaling; - } - - function accumulatePoseidonInternalRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - PoseidonInternalParams memory ip; - - Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from( - 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 - ), - FrLib.from( - 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b - ), - FrLib.from( - 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 - ), - FrLib.from( - 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b - ) - ]; - - // add round constants - ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); - - // apply s-box round - ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1; - ip.u2 = wire(p, WIRE.W_R); - ip.u3 = wire(p, WIRE.W_O); - ip.u4 = wire(p, WIRE.W_4); - - // matrix mul with v = M_I * u 4 muls and 7 additions - ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4; - - ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; - - ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = - evals[24] + - ip.q_pos_by_scaling * - (ip.v1 - wire(p, WIRE.W_L_SHIFT)); - - ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = - evals[25] + - ip.q_pos_by_scaling * - (ip.v2 - wire(p, WIRE.W_R_SHIFT)); - - ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = - evals[26] + - ip.q_pos_by_scaling * - (ip.v3 - wire(p, WIRE.W_O_SHIFT)); - - ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = - evals[27] + - ip.q_pos_by_scaling * - (ip.v4 - wire(p, WIRE.W_4_SHIFT)); - } - - // Batch subrelation evaluations using precomputed powers of alpha - // First subrelation is implicitly scaled by 1, subsequent ones use powers from the subrelationChallenges array - function scaleAndBatchSubrelations( - Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, - Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges - ) internal pure returns (Fr accumulator) { - accumulator = evaluations[0]; - - for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = - accumulator + - evaluations[i] * - subrelationChallenges[i - 1]; - } - } -} - -// Field arithmetic libraries - prevent littering the code with modmul / addmul - -library CommitmentSchemeLib { - using FrLib for Fr; - - // Avoid stack too deep - struct ShpleminiIntermediates { - Fr unshiftedScalar; - Fr shiftedScalar; - Fr unshiftedScalarNeg; - Fr shiftedScalarNeg; - // Scalar to be multiplied by [1]₁ - Fr constantTermAccumulator; - // Accumulator for powers of rho - Fr batchingChallenge; - // Linear combination of multilinear (sumcheck) evaluations and powers of rho - Fr batchedEvaluation; - Fr[4] denominators; - Fr[4] batchingScalars; - // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated - Fr posInvertedDenominator; - // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated - Fr negInvertedDenominator; - // ν^{2i} * 1/(z - r^{2^i}) - Fr scalingFactorPos; - // ν^{2i+1} * 1/(z + r^{2^i}) - Fr scalingFactorNeg; - // Fold_i(r^{2^i}) reconstructed by Verifier - Fr[] foldPosEvaluations; - } - - function computeSquares( - Fr r, - uint256 logN - ) internal pure returns (Fr[] memory) { - Fr[] memory squares = new Fr[](logN); - squares[0] = r; - for (uint256 i = 1; i < logN; ++i) { - squares[i] = squares[i - 1].sqr(); - } - return squares; - } - // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1 - - function computeFoldPosEvaluations( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges, - Fr batchedEvalAccumulator, - Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations, - Fr[] memory geminiEvalChallengePowers, - uint256 logSize - ) internal view returns (Fr[] memory) { - Fr[] memory foldPosEvaluations = new Fr[](logSize); - for (uint256 i = logSize; i > 0; --i) { - Fr challengePower = geminiEvalChallengePowers[i - 1]; - Fr u = sumcheckUChallenges[i - 1]; - - Fr batchedEvalRoundAcc = ((challengePower * - batchedEvalAccumulator * - Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); - // Divide by the denominator - batchedEvalRoundAcc = - batchedEvalRoundAcc * - (challengePower * (ONE - u) + u).invert(); - - batchedEvalAccumulator = batchedEvalRoundAcc; - foldPosEvaluations[i - 1] = batchedEvalRoundAcc; - } - return foldPosEvaluations; - } -} - -uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q - -function bytes32ToString(bytes32 value) pure returns (string memory result) { - bytes memory alphabet = "0123456789abcdef"; - - bytes memory str = new bytes(66); - str[0] = "0"; - str[1] = "x"; - for (uint256 i = 0; i < 32; i++) { - str[2 + i * 2] = alphabet[uint8(value[i] >> 4)]; - str[3 + i * 2] = alphabet[uint8(value[i] & 0x0f)]; - } - result = string(str); -} - -// Fr utility - -function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { - scalar = FrLib.fromBytes32(bytes32(proofSection)); -} - -// EC Point utilities -function bytesToG1Point( - bytes calldata proofSection -) pure returns (Honk.G1Point memory point) { - point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, - y: uint256(bytes32(proofSection[0x20:0x40])) % Q - }); -} - -function negateInplace( - Honk.G1Point memory point -) pure returns (Honk.G1Point memory) { - point.y = (Q - point.y) % Q; - return point; -} - -/** - * Convert the pairing points to G1 points. - * - * The pairing points are serialised as an array of 68 bit limbs representing two points - * The lhs of a pairing operation and the rhs of a pairing operation - * - * There are 4 fields for each group element, leaving 8 fields for each side of the pairing. - * - * @param pairingPoints The pairing points to convert. - * @return lhs - * @return rhs - */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { - uint256 lhsX = Fr.unwrap(pairingPoints[0]); - lhsX |= Fr.unwrap(pairingPoints[1]) << 68; - lhsX |= Fr.unwrap(pairingPoints[2]) << 136; - lhsX |= Fr.unwrap(pairingPoints[3]) << 204; - lhs.x = lhsX; - - uint256 lhsY = Fr.unwrap(pairingPoints[4]); - lhsY |= Fr.unwrap(pairingPoints[5]) << 68; - lhsY |= Fr.unwrap(pairingPoints[6]) << 136; - lhsY |= Fr.unwrap(pairingPoints[7]) << 204; - lhs.y = lhsY; - - uint256 rhsX = Fr.unwrap(pairingPoints[8]); - rhsX |= Fr.unwrap(pairingPoints[9]) << 68; - rhsX |= Fr.unwrap(pairingPoints[10]) << 136; - rhsX |= Fr.unwrap(pairingPoints[11]) << 204; - rhs.x = rhsX; - - uint256 rhsY = Fr.unwrap(pairingPoints[12]); - rhsY |= Fr.unwrap(pairingPoints[13]) << 68; - rhsY |= Fr.unwrap(pairingPoints[14]) << 136; - rhsY |= Fr.unwrap(pairingPoints[15]) << 204; - rhs.y = rhsY; -} - -/** - * Hash the pairing inputs from the present verification context with those extracted from the public inputs. - * - * @param proofPairingPoints Pairing points from the proof - (public inputs). - * @param accLhs Accumulator point for the left side - result of shplemini. - * @param accRhs Accumulator point for the right side - result of shplemini. - * @return recursionSeparator The recursion separator - generated from hashing the above. - */ -function generateRecursionSeparator( - Fr[PAIRING_POINTS_SIZE] memory proofPairingPoints, - Honk.G1Point memory accLhs, - Honk.G1Point memory accRhs -) pure returns (Fr recursionSeparator) { - // hash the proof aggregated X - // hash the proof aggregated Y - // hash the accum X - // hash the accum Y - - ( - Honk.G1Point memory proofLhs, - Honk.G1Point memory proofRhs - ) = convertPairingPointsToG1(proofPairingPoints); - - uint256[8] memory recursionSeparatorElements; - - // Proof points - recursionSeparatorElements[0] = proofLhs.x; - recursionSeparatorElements[1] = proofLhs.y; - recursionSeparatorElements[2] = proofRhs.x; - recursionSeparatorElements[3] = proofRhs.y; - - // Accumulator points - recursionSeparatorElements[4] = accLhs.x; - recursionSeparatorElements[5] = accLhs.y; - recursionSeparatorElements[6] = accRhs.x; - recursionSeparatorElements[7] = accRhs.y; - - recursionSeparator = FrLib.fromBytes32( - keccak256(abi.encodePacked(recursionSeparatorElements)) - ); -} - -/** - * G1 Mul with Separator - * Using the ecAdd and ecMul precompiles - * - * @param basePoint The point to multiply. - * @param other The other point to add. - * @param recursionSeperator The separator to use for the multiplication. - * @return `(recursionSeperator * basePoint) + other`. - */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { - Honk.G1Point memory result; - - result = ecMul(recursionSeperator, basePoint); - result = ecAdd(result, other); - - return result; -} - -/** - * G1 Mul - * Takes a Fr value and a G1 point and uses the ecMul precompile to return the result. - * - * @param value The value to multiply the point by. - * @param point The point to multiply. - * @return result The result of the multiplication. - */ -function ecMul( - Fr value, - Honk.G1Point memory point -) view returns (Honk.G1Point memory) { - Honk.G1Point memory result; - - assembly { - let free := mload(0x40) - // Write the point into memory (two 32 byte words) - // Memory layout: - // Address | value - // free | point.x - // free + 0x20| point.y - mstore(free, mload(point)) - mstore(add(free, 0x20), mload(add(point, 0x20))) - // Write the scalar into memory (one 32 byte word) - // Memory layout: - // Address | value - // free + 0x40| value - mstore(add(free, 0x40), value) - - // Call the ecMul precompile, it takes in the following - // [point.x, point.y, scalar], and returns the result back into the free memory location. - let success := staticcall(gas(), 0x07, free, 0x60, free, 0x40) - if iszero(success) { - revert(0, 0) - } - // Copy the result of the multiplication back into the result memory location. - // Memory layout: - // Address | value - // result | result.x - // result + 0x20| result.y - mstore(result, mload(free)) - mstore(add(result, 0x20), mload(add(free, 0x20))) - - mstore(0x40, add(free, 0x60)) - } - - return result; -} - -/** - * G1 Add - * Takes two G1 points and uses the ecAdd precompile to return the result. - * - * @param lhs The left hand side of the addition. - * @param rhs The right hand side of the addition. - * @return result The result of the addition. - */ -function ecAdd( - Honk.G1Point memory lhs, - Honk.G1Point memory rhs -) view returns (Honk.G1Point memory) { - Honk.G1Point memory result; - - assembly { - let free := mload(0x40) - // Write lhs into memory (two 32 byte words) - // Memory layout: - // Address | value - // free | lhs.x - // free + 0x20| lhs.y - mstore(free, mload(lhs)) - mstore(add(free, 0x20), mload(add(lhs, 0x20))) - - // Write rhs into memory (two 32 byte words) - // Memory layout: - // Address | value - // free + 0x40| rhs.x - // free + 0x60| rhs.y - mstore(add(free, 0x40), mload(rhs)) - mstore(add(free, 0x60), mload(add(rhs, 0x20))) - - // Call the ecAdd precompile, it takes in the following - // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. - let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } - - // Copy the result of the addition back into the result memory location. - // Memory layout: - // Address | value - // result | result.x - // result + 0x20| result.y - mstore(result, mload(free)) - mstore(add(result, 0x20), mload(add(free, 0x20))) - - mstore(0x40, add(free, 0x80)) - } - - return result; -} - -function validateOnCurve(Honk.G1Point memory point) pure { - uint256 x = point.x; - uint256 y = point.y; - - bool success = false; - assembly { - let xx := mulmod(x, x, Q) - success := eq(mulmod(y, y, Q), addmod(mulmod(x, xx, Q), 3, Q)) - } - - require(success, "point is not on the curve"); -} - -function pairing( - Honk.G1Point memory rhs, - Honk.G1Point memory lhs -) view returns (bool decodedResult) { - bytes memory input = abi.encodePacked( - rhs.x, - rhs.y, - // Fixed G2 point - uint256( - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - ), - uint256( - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ), - uint256( - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - ), - uint256( - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ), - lhs.x, - lhs.y, - // G2 point from VK - uint256( - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 - ), - uint256( - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ), - uint256( - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 - ), - uint256( - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ) - ); - - (bool success, bytes memory result) = address(0x08).staticcall(input); - decodedResult = success && abi.decode(result, (bool)); -} - -// Field arithmetic libraries - prevent littering the code with modmul / addmul - -abstract contract BaseZKHonkVerifier is IVerifier { - using FrLib for Fr; - - uint256 immutable $N; - uint256 immutable $LOG_N; - uint256 immutable $VK_HASH; - uint256 immutable $NUM_PUBLIC_INPUTS; - uint256 immutable $MSMSize; - - constructor( - uint256 _N, - uint256 _logN, - uint256 _vkHash, - uint256 _numPublicInputs - ) { - $N = _N; - $LOG_N = _logN; - $VK_HASH = _vkHash; - $NUM_PUBLIC_INPUTS = _numPublicInputs; - $MSMSize = NUMBER_UNSHIFTED_ZK + _logN + LIBRA_COMMITMENTS + 2; - } - - // Errors - error ProofLengthWrong(); - error ProofLengthWrongWithLogN( - uint256 logN, - uint256 actualLength, - uint256 expectedLength - ); - error PublicInputsLengthWrong(); - error SumcheckFailed(); - error ShpleminiFailed(); - error GeminiChallengeInSubgroup(); - error ConsistencyCheckFailed(); - - // Constants for proof length calculation (matching UltraKeccakZKFlavor) - uint256 constant NUM_WITNESS_ENTITIES = 8 + NUM_MASKING_POLYNOMIALS; - uint256 constant NUM_ELEMENTS_COMM = 2; // uint256 elements for curve points - uint256 constant NUM_ELEMENTS_FR = 1; // uint256 elements for field elements - uint256 constant NUM_LIBRA_EVALUATIONS = 4; // libra evaluations - - // Calculate proof size based on log_n (matching UltraKeccakZKFlavor formula) - function calculateProofSize(uint256 logN) internal pure returns (uint256) { - // Witness and Libra commitments - uint256 proofLength = NUM_WITNESS_ENTITIES * NUM_ELEMENTS_COMM; // witness commitments - proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking - - // Sumcheck - proofLength += - logN * - ZK_BATCHED_RELATION_PARTIAL_LENGTH * - NUM_ELEMENTS_FR; // sumcheck univariates - proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations - - // Libra and Gemini - proofLength += NUM_ELEMENTS_FR * 2; // Libra sum, claimed eval - proofLength += logN * NUM_ELEMENTS_FR; // Gemini a evaluations - proofLength += NUM_LIBRA_EVALUATIONS * NUM_ELEMENTS_FR; // libra evaluations - - // PCS commitments - proofLength += (logN - 1) * NUM_ELEMENTS_COMM; // Gemini Fold commitments - proofLength += NUM_ELEMENTS_COMM * 2; // Shplonk Q and KZG W commitments - - // Pairing points - proofLength += PAIRING_POINTS_SIZE; // pairing inputs carried on public inputs - - return proofLength; - } - - uint256 constant SHIFTED_COMMITMENTS_START = 30; - - function loadVerificationKey() - internal - pure - virtual - returns (Honk.VerificationKey memory); - - function verify( - bytes calldata proof, - bytes32[] calldata publicInputs - ) public view override returns (bool verified) { - // Calculate expected proof size based on $LOG_N - uint256 expectedProofSize = calculateProofSize($LOG_N); - - // Check the received proof is the expected size where each field element is 32 bytes - if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN( - $LOG_N, - proof.length, - expectedProofSize * 32 - ); - } - - Honk.VerificationKey memory vk = loadVerificationKey(); - Honk.ZKProof memory p = ZKTranscriptLib.loadProof(proof, $LOG_N); - - if (publicInputs.length != vk.publicInputsSize - PAIRING_POINTS_SIZE) { - revert PublicInputsLengthWrong(); - } - - // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript( - p, - publicInputs, - $VK_HASH, - $NUM_PUBLIC_INPUTS, - $LOG_N - ); - - // Derive public input delta - t.relationParameters.publicInputsDelta = computePublicInputDelta( - publicInputs, - p.pairingPointObject, - t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, - 1 - ); - - // Sumcheck - if (!verifySumcheck(p, t)) revert SumcheckFailed(); - - if (!verifyShplemini(p, vk, t)) revert ShpleminiFailed(); - - verified = true; - } - - uint256 constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28; - - function computePublicInputDelta( - bytes32[] memory publicInputs, - Fr[PAIRING_POINTS_SIZE] memory pairingPointObject, - Fr beta, - Fr gamma, - uint256 offset - ) internal view returns (Fr publicInputDelta) { - Fr numerator = Fr.wrap(1); - Fr denominator = Fr.wrap(1); - - Fr numeratorAcc = gamma + - (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); - Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); - - { - for ( - uint256 i = 0; - i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; - i++ - ) { - Fr pubInput = FrLib.fromBytes32(publicInputs[i]); - - numerator = numerator * (numeratorAcc + pubInput); - denominator = denominator * (denominatorAcc + pubInput); - - numeratorAcc = numeratorAcc + beta; - denominatorAcc = denominatorAcc - beta; - } - - for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - Fr pubInput = pairingPointObject[i]; - - numerator = numerator * (numeratorAcc + pubInput); - denominator = denominator * (denominatorAcc + pubInput); - - numeratorAcc = numeratorAcc + beta; - denominatorAcc = denominatorAcc - beta; - } - } - - // Fr delta = numerator / denominator; // TOOO: batch invert later? - publicInputDelta = FrLib.div(numerator, denominator); - } - - function verifySumcheck( - Honk.ZKProof memory proof, - ZKTranscript memory tp - ) internal view returns (bool verified) { - Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 - Fr powPartialEvaluation = Fr.wrap(1); - - // We perform sumcheck reductions over log n rounds ( the multivariate degree ) - for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory roundUnivariate = proof.sumcheckUnivariates[round]; - Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; - if (totalSum != roundTargetSum) revert SumcheckFailed(); - - Fr roundChallenge = tp.sumCheckUChallenges[round]; - - // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum( - roundUnivariate, - roundChallenge - ); - powPartialEvaluation = - powPartialEvaluation * - (Fr.wrap(1) + - roundChallenge * - (tp.gateChallenges[round] - Fr.wrap(1))); - } - - // Last round - // For ZK flavors: sumcheckEvaluations has 42 elements - // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations - Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; - for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[ - i + NUM_MASKING_POLYNOMIALS - ]; // Skip gemini_masking_poly at index 0 - } - Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation - ); - - Fr evaluation = Fr.wrap(1); - for (uint256 i = 2; i < $LOG_N; i++) { - evaluation = evaluation * tp.sumCheckUChallenges[i]; - } - - grandHonkRelationSum = - grandHonkRelationSum * - (Fr.wrap(1) - evaluation) + - proof.libraEvaluation * - tp.libraChallenge; - verified = (grandHonkRelationSum == roundTargetSum); - } - - // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000000240 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ) - ]; - - // To compute the next target sum, we evaluate the given univariate at a point u (challenge). - - // Performing Barycentric evaluations - // Compute B(x) - Fr numeratorValue = Fr.wrap(1); - for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i)); - } - - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; - for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert( - BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * - (roundChallenge - Fr.wrap(i)) - ); - } - - for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = - targetSum + - roundUnivariates[i] * - denominatorInverses[i]; - } - - // Scale the sum by the value of B(x) - targetSum = targetSum * numeratorValue; - } - - uint256 constant LIBRA_COMMITMENTS = 3; - uint256 constant LIBRA_EVALUATIONS = 4; - uint256 constant LIBRA_UNIVARIATES_LENGTH = 9; - - struct PairingInputs { - Honk.G1Point P_0; - Honk.G1Point P_1; - } - - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { - CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack - - // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib - .computeSquares(tp.geminiR, $LOG_N); - // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings - Fr[] memory scalars = new Fr[]($MSMSize); - Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[0]).invert(); - - mem.unshiftedScalar = - mem.posInvertedDenominator + - (tp.shplonkNu * mem.negInvertedDenominator); - mem.shiftedScalar = - tp.geminiR.invert() * - (mem.posInvertedDenominator - - (tp.shplonkNu * mem.negInvertedDenominator)); - - scalars[0] = Fr.wrap(1); - commitments[0] = proof.shplonkQ; - - /* Batch multivariate opening claims, shifted and unshifted - * The vector of scalars is populated as follows: - * \f[ - * \left( - * - \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), - * \ldots, - * - \rho^{i+k-1} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), - * - \rho^{i+k} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right), - * \ldots, - * - \rho^{k+m-1} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) - * \right) - * \f] - * - * The following vector is concatenated to the vector of commitments: - * \f[ - * f_0, \ldots, f_{m-1}, f_{\text{shift}, 0}, \ldots, f_{\text{shift}, k-1} - * \f] - * - * Simultaneously, the evaluation of the multilinear polynomial - * \f[ - * \sum \rho^i \cdot f_i + \sum \rho^{i+k} \cdot f_{\text{shift}, i} - * \f] - * at the challenge point \f$ (u_0,\ldots, u_{n-1}) \f$ is computed. - * - * This approach minimizes the number of iterations over the commitments to multilinear polynomials - * and eliminates the need to store the powers of \f$ \rho \f$. - */ - // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] - // Start batching challenge at 1, not rho, to match non-ZK pattern - mem.batchingChallenge = Fr.wrap(1); - mem.batchedEvaluation = Fr.wrap(0); - - mem.unshiftedScalarNeg = mem.unshiftedScalar.neg(); - mem.shiftedScalarNeg = mem.shiftedScalar.neg(); - - // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) - for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { - scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * - mem.batchingChallenge); - mem.batchingChallenge = mem.batchingChallenge * tp.rho; - } - // g commitments are accumulated at r - // For each of the to be shifted commitments perform the shift in place by - // adding to the unshifted value. - // We do so, as the values are to be used in batchMul later, and as - // `a * c + b * c = (a + b) * c` this will allow us to reduce memory and compute. - // Applied to w1, w2, w3, w4 and zPerm - for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) { - uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; - uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - - scalars[scalarOff] = - scalars[scalarOff] + - (mem.shiftedScalarNeg * mem.batchingChallenge); - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[evaluationOff] * - mem.batchingChallenge); - mem.batchingChallenge = mem.batchingChallenge * tp.rho; - } - - commitments[1] = proof.geminiMaskingPoly; - - commitments[2] = vk.qm; - commitments[3] = vk.qc; - commitments[4] = vk.ql; - commitments[5] = vk.qr; - commitments[6] = vk.qo; - commitments[7] = vk.q4; - commitments[8] = vk.qLookup; - commitments[9] = vk.qArith; - commitments[10] = vk.qDeltaRange; - commitments[11] = vk.qElliptic; - commitments[12] = vk.qMemory; - commitments[13] = vk.qNnf; - commitments[14] = vk.qPoseidon2External; - commitments[15] = vk.qPoseidon2Internal; - commitments[16] = vk.s1; - commitments[17] = vk.s2; - commitments[18] = vk.s3; - commitments[19] = vk.s4; - commitments[20] = vk.id1; - commitments[21] = vk.id2; - commitments[22] = vk.id3; - commitments[23] = vk.id4; - commitments[24] = vk.t1; - commitments[25] = vk.t2; - commitments[26] = vk.t3; - commitments[27] = vk.t4; - commitments[28] = vk.lagrangeFirst; - commitments[29] = vk.lagrangeLast; - - // Accumulate proof points - commitments[30] = proof.w1; - commitments[31] = proof.w2; - commitments[32] = proof.w3; - commitments[33] = proof.w4; - commitments[34] = proof.zPerm; - commitments[35] = proof.lookupInverses; - commitments[36] = proof.lookupReadCounts; - commitments[37] = proof.lookupReadTags; - - /* Batch gemini claims from the prover - * place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from - * aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars - * - * 1. Moves the vector - * \f[ - * \left( \text{com}(A_1), \text{com}(A_2), \ldots, \text{com}(A_{n-1}) \right) - * \f] - * to the 'commitments' vector. - * - * 2. Computes the scalars: - * \f[ - * \frac{\nu^{2}}{z + r^2}, \frac{\nu^3}{z + r^4}, \ldots, \frac{\nu^{n-1}}{z + r^{2^{n-1}}} - * \f] - * and places them into the 'scalars' vector. - * - * 3. Accumulates the summands of the constant term: - * \f[ - * \sum_{i=2}^{n-1} \frac{\nu^{i} \cdot A_i(-r^{2^i})}{z + r^{2^i}} - * \f] - * and adds them to the 'constant_term_accumulator'. - */ - - // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: - // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib - .computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); - - mem.constantTermAccumulator = - foldPosEvaluations[0] * - mem.posInvertedDenominator; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - (proof.geminiAEvaluations[0] * - tp.shplonkNu * - mem.negInvertedDenominator); - - mem.batchingChallenge = tp.shplonkNu.sqr(); - uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; - - // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1; - // Compute scalar multipliers for each fold commitment - for (uint256 i = 0; i < $LOG_N - 1; ++i) { - bool dummy_round = i >= ($LOG_N - 1); - - if (!dummy_round) { - // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[i + 1]).invert(); - - // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = - mem.batchingChallenge * - mem.posInvertedDenominator; - mem.scalingFactorNeg = - mem.batchingChallenge * - tp.shplonkNu * - mem.negInvertedDenominator; - scalars[boundary + i] = - mem.scalingFactorNeg.neg() + - mem.scalingFactorPos.neg(); - - // Accumulate the const term contribution given by - // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * - proof.geminiAEvaluations[i + 1]; - accumContribution = - accumContribution + - mem.scalingFactorPos * - foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - accumContribution; - } - // Update the running power of v - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; - - commitments[boundary + i] = proof.geminiFoldComms[i]; - } - - boundary += $LOG_N - 1; - - // Finalize the batch opening claim - mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div( - tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR - ); - mem.denominators[2] = mem.denominators[0]; - mem.denominators[3] = mem.denominators[0]; - - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; - for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { - Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; - mem.batchingScalars[i] = scalingFactor.neg(); - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - scalingFactor * - proof.libraPolyEvals[i]; - } - scalars[boundary] = mem.batchingScalars[0]; - scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; - scalars[boundary + 2] = mem.batchingScalars[3]; - - for (uint256 i = 0; i < LIBRA_COMMITMENTS; i++) { - commitments[boundary++] = proof.libraCommitments[i]; - } - - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); - scalars[boundary++] = mem.constantTermAccumulator; - - if ( - !checkEvalsConsistency( - proof.libraPolyEvals, - tp.geminiR, - tp.sumCheckUChallenges, - proof.libraEvaluation - ) - ) { - revert ConsistencyCheckFailed(); - } - - Honk.G1Point memory quotient_commitment = proof.kzgQuotient; - - commitments[boundary] = quotient_commitment; - scalars[boundary] = tp.shplonkZ; // evaluation challenge - - PairingInputs memory pair; - pair.P_0 = batchMul(commitments, scalars); - pair.P_1 = negateInplace(quotient_commitment); - - // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator( - proof.pairingPointObject, - pair.P_0, - pair.P_1 - ); - ( - Honk.G1Point memory P_0_other, - Honk.G1Point memory P_1_other - ) = convertPairingPointsToG1(proof.pairingPointObject); - - // Validate the points from the proof are on the curve - validateOnCurve(P_0_other); - validateOnCurve(P_1_other); - - // accumulate with aggregate points in proof - pair.P_0 = mulWithSeperator(pair.P_0, P_0_other, recursionSeparator); - pair.P_1 = mulWithSeperator(pair.P_1, P_1_other, recursionSeparator); - - return pairing(pair.P_0, pair.P_1); - } - - struct SmallSubgroupIpaIntermediates { - Fr[SUBGROUP_SIZE] challengePolyLagrange; - Fr challengePolyEval; - Fr lagrangeFirst; - Fr lagrangeLast; - Fr rootPower; - Fr[SUBGROUP_SIZE] denominators; // this has to disappear - Fr diff; - } - - function checkEvalsConsistency( - Fr[LIBRA_EVALUATIONS] memory libraPolyEvals, - Fr geminiR, - Fr[CONST_PROOF_SIZE_LOG_N] memory uChallenges, - Fr libraEval - ) internal view returns (bool check) { - Fr one = Fr.wrap(1); - Fr vanishingPolyEval = geminiR.pow(SUBGROUP_SIZE) - one; - if (vanishingPolyEval == Fr.wrap(0)) { - revert GeminiChallengeInSubgroup(); - } - - SmallSubgroupIpaIntermediates memory mem; - mem.challengePolyLagrange[0] = one; - for (uint256 round = 0; round < $LOG_N; round++) { - uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; - mem.challengePolyLagrange[currIdx] = one; - for ( - uint256 idx = currIdx + 1; - idx < currIdx + LIBRA_UNIVARIATES_LENGTH; - idx++ - ) { - mem.challengePolyLagrange[idx] = - mem.challengePolyLagrange[idx - 1] * - uChallenges[round]; - } - } - - mem.rootPower = one; - mem.challengePolyEval = Fr.wrap(0); - for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { - mem.denominators[idx] = mem.rootPower * geminiR - one; - mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = - mem.challengePolyEval + - mem.challengePolyLagrange[idx] * - mem.denominators[idx]; - mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; - } - - Fr numerator = vanishingPolyEval * Fr.wrap(SUBGROUP_SIZE).invert(); - mem.challengePolyEval = mem.challengePolyEval * numerator; - mem.lagrangeFirst = mem.denominators[0] * numerator; - mem.lagrangeLast = mem.denominators[SUBGROUP_SIZE - 1] * numerator; - - mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - - libraPolyEvals[2] - - libraPolyEvals[0] * - mem.challengePolyEval); - mem.diff = - mem.diff + - mem.lagrangeLast * - (libraPolyEvals[2] - libraEval) - - vanishingPolyEval * - libraPolyEvals[3]; - - check = mem.diff == Fr.wrap(0); - } - - // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[] memory base, - Fr[] memory scalars - ) internal view returns (Honk.G1Point memory result) { - uint256 limit = $MSMSize; - - // Validate all points are on the curve - for (uint256 i = 0; i < limit; ++i) { - validateOnCurve(base[i]); - } - - bool success = true; - assembly { - let free := mload(0x40) - - let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { - // Get loop offsets - let base_base := add(base, mul(count, 0x20)) - let scalar_base := add(scalars, mul(count, 0x20)) - - mstore(add(free, 0x40), mload(mload(base_base))) - mstore(add(free, 0x60), mload(add(0x20, mload(base_base)))) - // Add scalar - mstore(add(free, 0x80), mload(scalar_base)) - - success := and( - success, - staticcall( - gas(), - 7, - add(free, 0x40), - 0x60, - add(free, 0x40), - 0x40 - ) - ) - // accumulator = accumulator + accumulator_2 - success := and( - success, - staticcall(gas(), 6, free, 0x80, free, 0x40) - ) - } - - // Return the result - mstore(result, mload(free)) - mstore(add(result, 0x20), mload(add(free, 0x20))) - } - - require(success, ShpleminiFailed()); - } -} - -contract ThresholdDecryptedSharesAggregationVerifier is - BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) -{ - function loadVerificationKey() - internal - pure - override - returns (Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); - } -} diff --git a/packages/enclave-contracts/ignition/modules/bfvDecryptionVerifier.ts b/packages/enclave-contracts/ignition/modules/bfvDecryptionVerifier.ts index f6a33872a4..964c9e324e 100644 --- a/packages/enclave-contracts/ignition/modules/bfvDecryptionVerifier.ts +++ b/packages/enclave-contracts/ignition/modules/bfvDecryptionVerifier.ts @@ -5,20 +5,15 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; -import recursiveAggregationFoldVerifierModule from "./recursiveAggregationFoldVerifier"; -import thresholdDecryptedSharesAggregationVerifierModule from "./thresholdDecryptedSharesAggregationVerifier"; +import decryptionAggregatorVerifierModule from "./decryptionAggregatorVerifier"; export default buildModule("BfvDecryptionVerifier", (m) => { - const { thresholdDecryptedSharesAggregationVerifier } = m.useModule( - thresholdDecryptedSharesAggregationVerifierModule, - ); - const { recursiveAggregationFoldVerifier } = m.useModule( - recursiveAggregationFoldVerifierModule, + const { decryptionAggregatorVerifier } = m.useModule( + decryptionAggregatorVerifierModule, ); const bfvDecryptionVerifier = m.contract("BfvDecryptionVerifier", [ - thresholdDecryptedSharesAggregationVerifier, - recursiveAggregationFoldVerifier, + decryptionAggregatorVerifier, ]); return { bfvDecryptionVerifier }; diff --git a/packages/enclave-contracts/ignition/modules/bfvPkVerifier.ts b/packages/enclave-contracts/ignition/modules/bfvPkVerifier.ts index b08e76dad1..afc635b170 100644 --- a/packages/enclave-contracts/ignition/modules/bfvPkVerifier.ts +++ b/packages/enclave-contracts/ignition/modules/bfvPkVerifier.ts @@ -5,21 +5,12 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; -import recursiveAggregationFoldVerifierModule from "./recursiveAggregationFoldVerifier"; -import thresholdPkAggregationVerifierModule from "./thresholdPkAggregationVerifier"; +import dkgAggregatorVerifierModule from "./dkgAggregatorVerifier"; export default buildModule("BfvPkVerifier", (m) => { - const { thresholdPkAggregationVerifier } = m.useModule( - thresholdPkAggregationVerifierModule, - ); - const { recursiveAggregationFoldVerifier } = m.useModule( - recursiveAggregationFoldVerifierModule, - ); + const { dkgAggregatorVerifier } = m.useModule(dkgAggregatorVerifierModule); - const bfvPkVerifier = m.contract("BfvPkVerifier", [ - thresholdPkAggregationVerifier, - recursiveAggregationFoldVerifier, - ]); + const bfvPkVerifier = m.contract("BfvPkVerifier", [dkgAggregatorVerifier]); return { bfvPkVerifier }; }) as any; diff --git a/packages/enclave-contracts/ignition/modules/thresholdPkAggregationVerifier.ts b/packages/enclave-contracts/ignition/modules/decryptionAggregatorVerifier.ts similarity index 57% rename from packages/enclave-contracts/ignition/modules/thresholdPkAggregationVerifier.ts rename to packages/enclave-contracts/ignition/modules/decryptionAggregatorVerifier.ts index 7e8fd4c3f1..20ed511a5b 100644 --- a/packages/enclave-contracts/ignition/modules/thresholdPkAggregationVerifier.ts +++ b/packages/enclave-contracts/ignition/modules/decryptionAggregatorVerifier.ts @@ -5,10 +5,10 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; -export default buildModule("ThresholdPkAggregationVerifier", (m) => { - const thresholdPkAggregationVerifier = m.contract( - "ThresholdPkAggregationVerifier", +export default buildModule("DecryptionAggregatorVerifier", (m) => { + const decryptionAggregatorVerifier = m.contract( + "DecryptionAggregatorVerifier", ); - return { thresholdPkAggregationVerifier }; + return { decryptionAggregatorVerifier }; }) as any; diff --git a/packages/enclave-contracts/ignition/modules/recursiveAggregationFoldVerifier.ts b/packages/enclave-contracts/ignition/modules/dkgAggregatorVerifier.ts similarity index 55% rename from packages/enclave-contracts/ignition/modules/recursiveAggregationFoldVerifier.ts rename to packages/enclave-contracts/ignition/modules/dkgAggregatorVerifier.ts index fb48071a68..2162b6e1dd 100644 --- a/packages/enclave-contracts/ignition/modules/recursiveAggregationFoldVerifier.ts +++ b/packages/enclave-contracts/ignition/modules/dkgAggregatorVerifier.ts @@ -5,10 +5,8 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; -export default buildModule("RecursiveAggregationFoldVerifier", (m) => { - const recursiveAggregationFoldVerifier = m.contract( - "RecursiveAggregationFoldVerifier", - ); +export default buildModule("DkgAggregatorVerifier", (m) => { + const dkgAggregatorVerifier = m.contract("DkgAggregatorVerifier"); - return { recursiveAggregationFoldVerifier }; + return { dkgAggregatorVerifier }; }) as any; diff --git a/packages/enclave-contracts/ignition/modules/thresholdDecryptedSharesAggregationVerifier.ts b/packages/enclave-contracts/ignition/modules/thresholdDecryptedSharesAggregationVerifier.ts deleted file mode 100644 index f890339413..0000000000 --- a/packages/enclave-contracts/ignition/modules/thresholdDecryptedSharesAggregationVerifier.ts +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. -import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; - -export default buildModule( - "ThresholdDecryptedSharesAggregationVerifier", - (m) => { - const thresholdDecryptedSharesAggregationVerifier = m.contract( - "ThresholdDecryptedSharesAggregationVerifier", - ); - - return { thresholdDecryptedSharesAggregationVerifier }; - }, -) as any; diff --git a/packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts b/packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts index 9faecfc7fc..8cd9687708 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts @@ -21,23 +21,12 @@ export const deployAndSaveBfvDecryptionVerifier = async ( const chain = hre.globalOptions.network ?? "localhost"; const circuitVerifierArgs = readDeploymentArgs( - "ThresholdDecryptedSharesAggregationVerifier", + "DecryptionAggregatorVerifier", chain, ); if (!circuitVerifierArgs?.address) { throw new Error( - "ThresholdDecryptedSharesAggregationVerifier must be deployed first. " + - "Run deployAndSaveAllVerifiers or deploy verifiers.", - ); - } - - const foldVerifierArgs = readDeploymentArgs( - "RecursiveAggregationFoldVerifier", - chain, - ); - if (!foldVerifierArgs?.address) { - throw new Error( - "RecursiveAggregationFoldVerifier must be deployed first. " + + "DecryptionAggregatorVerifier must be deployed first. " + "Run deployAndSaveAllVerifiers or deploy verifiers.", ); } @@ -59,7 +48,6 @@ export const deployAndSaveBfvDecryptionVerifier = async ( ); const bfvDecryptionVerifier = await bfvDecryptionVerifierFactory.deploy( circuitVerifierArgs.address, - foldVerifierArgs.address, ); await bfvDecryptionVerifier.waitForDeployment(); diff --git a/packages/enclave-contracts/scripts/deployAndSave/bfvPkVerifier.ts b/packages/enclave-contracts/scripts/deployAndSave/bfvPkVerifier.ts index b2a3381075..e47245c7e8 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/bfvPkVerifier.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/bfvPkVerifier.ts @@ -21,23 +21,12 @@ export const deployAndSaveBfvPkVerifier = async ( const chain = networkName ?? "localhost"; const circuitVerifierArgs = readDeploymentArgs( - "ThresholdPkAggregationVerifier", + "DkgAggregatorVerifier", chain, ); if (!circuitVerifierArgs?.address) { throw new Error( - "ThresholdPkAggregationVerifier must be deployed first. " + - "Run deployAndSaveAllVerifiers or deploy verifiers.", - ); - } - - const foldVerifierArgs = readDeploymentArgs( - "RecursiveAggregationFoldVerifier", - chain, - ); - if (!foldVerifierArgs?.address) { - throw new Error( - "RecursiveAggregationFoldVerifier must be deployed first. " + + "DkgAggregatorVerifier must be deployed first. " + "Run deployAndSaveAllVerifiers or deploy verifiers.", ); } @@ -55,7 +44,6 @@ export const deployAndSaveBfvPkVerifier = async ( const bfvPkVerifierFactory = await ethers.getContractFactory("BfvPkVerifier"); const bfvPkVerifier = await bfvPkVerifierFactory.deploy( circuitVerifierArgs.address, - foldVerifierArgs.address, ); await bfvPkVerifier.waitForDeployment(); diff --git a/packages/enclave-contracts/scripts/deployEnclave.ts b/packages/enclave-contracts/scripts/deployEnclave.ts index 8971f91e8c..7d8c080a16 100644 --- a/packages/enclave-contracts/scripts/deployEnclave.ts +++ b/packages/enclave-contracts/scripts/deployEnclave.ts @@ -71,9 +71,8 @@ const DEFAULT_TIMEOUT_CONFIG = { }; /** Circuit names required for BFV ZK verification in this script */ -const THRESHOLD_DECRYPTED_SHARES_AGGREGATION_VERIFIER = - "ThresholdDecryptedSharesAggregationVerifier"; -const THRESHOLD_PK_AGGREGATION_VERIFIER = "ThresholdPkAggregationVerifier"; +const DKG_AGGREGATOR_VERIFIER = "DkgAggregatorVerifier"; +const DECRYPTION_AGGREGATOR_VERIFIER = "DecryptionAggregatorVerifier"; /** * Deploys the Enclave contracts @@ -340,8 +339,8 @@ export const deployEnclave = async ( console.log("Deploying circuit verifiers..."); verifierDeployments = await deployAndSaveAllVerifiers(hre); const requiredVerifierNames = [ - THRESHOLD_DECRYPTED_SHARES_AGGREGATION_VERIFIER, - THRESHOLD_PK_AGGREGATION_VERIFIER, + DKG_AGGREGATOR_VERIFIER, + DECRYPTION_AGGREGATOR_VERIFIER, ] as const; for (const name of requiredVerifierNames) { const addr = verifierDeployments[name]; diff --git a/packages/enclave-contracts/tasks/enclave.ts b/packages/enclave-contracts/tasks/enclave.ts index 384e7096e8..57c597e133 100644 --- a/packages/enclave-contracts/tasks/enclave.ts +++ b/packages/enclave-contracts/tasks/enclave.ts @@ -3,7 +3,7 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -import { BigNumberish, ZeroAddress, zeroPadValue } from "ethers"; +import { BigNumberish, ZeroAddress, ZeroHash, isHexString, zeroPadValue } from "ethers"; import fs from "fs"; import { task } from "hardhat/config"; import { ArgumentType } from "hardhat/types/arguments"; @@ -315,20 +315,21 @@ export const publishCommittee = task( type: ArgumentType.STRING, }) .addOption({ - name: "proof", + name: "pkCommitment", description: - "ABI-encoded pk proof (bytes rawProof, bytes32[] publicInputs); commitment is last input", + "Hash-based aggregated PK commitment (bytes32 hex); required even when proof aggregation is disabled", defaultValue: "", type: ArgumentType.STRING, }) .addOption({ - name: "foldProof", - description: "fold proof to publish", + name: "proof", + description: + "ABI-encoded DkgAggregator (EVM) proof (bytes rawProof, bytes32[] publicInputs); pass 0x when proof aggregation is disabled", defaultValue: "0x", type: ArgumentType.STRING, }) .setAction(async () => ({ - default: async ({ e3Id, nodes, publicKey, proof, foldProof }, hre) => { + default: async ({ e3Id, nodes, publicKey, pkCommitment, proof }, hre) => { const { deployAndSaveCiphernodeRegistryOwnable } = await import( "../scripts/deployAndSave/ciphernodeRegistryOwnable" ); @@ -353,16 +354,30 @@ export const publishCommittee = task( throw new Error("Invalid nodes format: no valid addresses found"); } - if (!proof) { - throw new Error("proof is required"); + if (!pkCommitment) { + throw new Error("pkCommitment is required"); + } + // pkCommitment is stored and emitted unconditionally by the registry, so off-chain + // consumers can read an unusable key if the values are inconsistent. Validate format + // and require both publicKey and pkCommitment to be present and non-zero. + if (!isHexString(pkCommitment, 32)) { + throw new Error( + `pkCommitment must be a 32-byte hex string (got ${pkCommitment})`, + ); + } + if (pkCommitment === ZeroHash) { + throw new Error("pkCommitment must not be the zero hash"); + } + if (!isHexString(publicKey) || publicKey === "0x") { + throw new Error("publicKey is required and must be a non-empty hex string"); } const tx = await ciphernodeRegistry.publishCommittee( e3Id, nodesToSend, publicKey, + pkCommitment, proof, - foldProof, ); console.log("Publishing committee... ", tx.hash); @@ -567,23 +582,8 @@ export const publishPlaintext = task( defaultValue: "", type: ArgumentType.STRING, }) - .addOption({ - name: "foldProof", - description: "fold proof to publish", - defaultValue: "0x", - type: ArgumentType.STRING, - }) - .addOption({ - name: "foldProofFile", - description: "file containing fold proof to publish", - defaultValue: "", - type: ArgumentType.STRING, - }) .setAction(async () => ({ - default: async ( - { e3Id, data, dataFile, proof, proofFile, foldProof, foldProofFile }, - hre, - ) => { + default: async ({ e3Id, data, dataFile, proof, proofFile }, hre) => { const { deployAndSaveEnclave } = await import( "../scripts/deployAndSave/enclave" ); @@ -600,23 +600,16 @@ export const publishPlaintext = task( } let proofToSend = proof; - let foldProofToSend = foldProof; if (proofFile) { const file = fs.readFileSync(proofFile); proofToSend = file.toString(); } - if (foldProofFile) { - const file = fs.readFileSync(foldProofFile); - foldProofToSend = file.toString(); - } - const tx = await enclave.publishPlaintextOutput( e3Id, dataToSend, proofToSend, - foldProofToSend, ); console.log("Publishing plaintext... ", tx.hash); diff --git a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts index 81ab713416..ea685b7778 100644 --- a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts @@ -58,8 +58,6 @@ function encodeProof(rawProof: string, publicInputs: string[]): string { return abiCoder.encode(["bytes", "bytes32[]"], [rawProof, publicInputs]); } -const EMPTY_FOLD = "0x"; - describe("BfvDecryptionVerifier", function () { const deployWithMockCircuit = async () => { const [owner] = await ethers.getSigners(); @@ -67,15 +65,10 @@ describe("BfvDecryptionVerifier", function () { MockCircuitVerifierModule, ); const mockAddr = await mockCircuitVerifier.getAddress(); - const mockFold = await ( - await ethers.getContractFactory("MockCircuitVerifier") - ).deploy(); - await mockFold.waitForDeployment(); - const mockFoldAddr = await mockFold.getAddress(); const bfvDecryptionVerifier = await ( await ethers.getContractFactory("BfvDecryptionVerifier") - ).deploy(mockAddr, mockFoldAddr); + ).deploy(mockAddr); await bfvDecryptionVerifier.waitForDeployment(); const dv = BfvDecryptionVerifierFactory.connect( @@ -83,24 +76,19 @@ describe("BfvDecryptionVerifier", function () { owner, ); const mc = MockCircuitVerifierFactory.connect(mockAddr, owner); - const mf = MockCircuitVerifierFactory.connect(mockFoldAddr, owner); - return { bfvDecryptionVerifier: dv, mockCircuit: mc, mockFold: mf }; + return { bfvDecryptionVerifier: dv, mockCircuit: mc }; }; - describe("reverts", function () { + describe("reverts / false", function () { it("reverts on invalid proof encoding", async function () { const { bfvDecryptionVerifier } = await loadFixture( deployWithMockCircuit, ); const plaintextHash = ethers.keccak256("0x1234"); - const invalidProof = "0xdeadbeef"; // not abi.encode(bytes, bytes32[]) + const invalidProof = "0xdeadbeef"; await expect( - bfvDecryptionVerifier.verify.staticCall( - plaintextHash, - invalidProof, - EMPTY_FOLD, - ), + bfvDecryptionVerifier.verify.staticCall(plaintextHash, invalidProof), ).to.be.revert(ethers); }); @@ -121,7 +109,6 @@ describe("BfvDecryptionVerifier", function () { const result = await bfvDecryptionVerifier.verify.staticCall( plaintextHash, proof, - EMPTY_FOLD, ); expect(result).to.equal(false); }); @@ -134,13 +121,12 @@ describe("BfvDecryptionVerifier", function () { const messageCoeffs = [1n, 2n, 3n]; const publicInputs = buildPublicInputsWithMessage(messageCoeffs); - const wrongHash = ethers.keccak256("0x0000"); // doesn't match message + const wrongHash = ethers.keccak256("0x0000"); const proof = encodeProof("0x01", publicInputs); const result = await bfvDecryptionVerifier.verify.staticCall( wrongHash, proof, - EMPTY_FOLD, ); expect(result).to.equal(false); }); @@ -159,28 +145,6 @@ describe("BfvDecryptionVerifier", function () { const result = await bfvDecryptionVerifier.verify.staticCall( plaintextHash, proof, - EMPTY_FOLD, - ); - expect(result).to.equal(false); - }); - - it("returns false when fold proof is present but fold verifier returns false", async function () { - const { bfvDecryptionVerifier, mockCircuit, mockFold } = - await loadFixture(deployWithMockCircuit); - await mockCircuit.setReturnValue(true); - await mockFold.setReturnValue(false); - - const messageCoeffs = [1n, 2n, 3n]; - const publicInputs = buildPublicInputsWithMessage(messageCoeffs); - const plaintextHash = plaintextToHash(messageCoeffs); - const foldPi = ["0x" + "01".repeat(32)]; - const foldProof = encodeProof("0x02", foldPi); - const proof = encodeProof("0x01", publicInputs); - - const result = await bfvDecryptionVerifier.verify.staticCall( - plaintextHash, - proof, - foldProof, ); expect(result).to.equal(false); }); @@ -201,7 +165,6 @@ describe("BfvDecryptionVerifier", function () { const result = await bfvDecryptionVerifier.verify.staticCall( plaintextHash, proof, - EMPTY_FOLD, ); expect(result).to.equal(true); }); @@ -223,28 +186,6 @@ describe("BfvDecryptionVerifier", function () { const result = await bfvDecryptionVerifier.verify.staticCall( plaintextHash, proof, - EMPTY_FOLD, - ); - expect(result).to.equal(true); - }); - - it("returns true when fold proof is present and fold verifies", async function () { - const { bfvDecryptionVerifier, mockCircuit, mockFold } = - await loadFixture(deployWithMockCircuit); - await mockCircuit.setReturnValue(true); - await mockFold.setReturnValue(true); - - const messageCoeffs = [1n, 2n, 3n]; - const publicInputs = buildPublicInputsWithMessage(messageCoeffs); - const plaintextHash = plaintextToHash(messageCoeffs); - const foldPi = ["0x" + "01".repeat(32)]; - const foldProof = encodeProof("0xaa", foldPi); - const proof = encodeProof("0x01", publicInputs); - - const result = await bfvDecryptionVerifier.verify.staticCall( - plaintextHash, - proof, - foldProof, ); expect(result).to.equal(true); }); diff --git a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts index 64c64a0e66..7d72c98028 100644 --- a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts @@ -20,8 +20,6 @@ function encodeProof(rawProof: string, publicInputs: string[]): string { return abiCoder.encode(["bytes", "bytes32[]"], [rawProof, publicInputs]); } -const EMPTY_FOLD = "0x"; - describe("BfvPkVerifier", function () { const deployWithMockCircuit = async () => { const [owner] = await ethers.getSigners(); @@ -29,15 +27,10 @@ describe("BfvPkVerifier", function () { MockCircuitVerifierModule, ); const mockAddr = await mockCircuitVerifier.getAddress(); - const mockFold = await ( - await ethers.getContractFactory("MockCircuitVerifier") - ).deploy(); - await mockFold.waitForDeployment(); - const mockFoldAddr = await mockFold.getAddress(); const bfvPkVerifier = await ( await ethers.getContractFactory("BfvPkVerifier") - ).deploy(mockAddr, mockFoldAddr); + ).deploy(mockAddr); await bfvPkVerifier.waitForDeployment(); const pk = BfvPkVerifierFactory.connect( @@ -45,110 +38,86 @@ describe("BfvPkVerifier", function () { owner, ); const mc = MockCircuitVerifierFactory.connect(mockAddr, owner); - const mf = MockCircuitVerifierFactory.connect(mockFoldAddr, owner); - return { bfvPkVerifier: pk, mockCircuit: mc, mockFold: mf }; + return { bfvPkVerifier: pk, mockCircuit: mc }; }; - describe("reverts", function () { + describe("reverts / false", function () { it("reverts on invalid proof encoding", async function () { const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); + const pkCommitment = ethers.keccak256("0x1234"); const invalidProof = "0xdeadbeef"; await expect( - bfvPkVerifier.verify.staticCall(invalidProof, EMPTY_FOLD), + bfvPkVerifier.verify.staticCall(pkCommitment, invalidProof), ).to.be.revert(ethers); }); - it("reverts when publicInputs is empty", async function () { + it("returns false when publicInputs is empty", async function () { const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); + const pkCommitment = ethers.keccak256("0x1234"); const proof = encodeProof("0x01", []); - await expect( - bfvPkVerifier.verify.staticCall(proof, EMPTY_FOLD), - ).to.be.revert(ethers); + const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + expect(result).to.equal(false); }); - it("reverts when circuit verifier returns false", async function () { + it("returns false when pkCommitment does not match last public input", async function () { const { bfvPkVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); - await mockCircuit.setReturnValue(false); + await mockCircuit.setReturnValue(true); - const commitment = ethers.keccak256("0x1234"); - const proof = encodeProof("0x01", [commitment]); + const pkCommitment = ethers.keccak256("0xabcd"); + const wrong = ethers.keccak256("0x1234"); + const proof = encodeProof("0x01", [wrong]); - await expect( - bfvPkVerifier.verify.staticCall(proof, EMPTY_FOLD), - ).to.be.revert(ethers); + const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + expect(result).to.equal(false); }); - it("reverts when fold verifier returns false and fold proof is present", async function () { - const { bfvPkVerifier, mockCircuit, mockFold } = await loadFixture( + it("returns false when circuit verifier returns false", async function () { + const { bfvPkVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); - await mockCircuit.setReturnValue(true); - await mockFold.setReturnValue(false); + await mockCircuit.setReturnValue(false); - const commitment = ethers.keccak256("0x1234"); - const proof = encodeProof("0x01", [commitment]); - const foldPi = ["0x" + "01".repeat(32)]; - const foldProof = encodeProof("0x02", foldPi); + const pkCommitment = ethers.keccak256("0xabcd"); + const proof = encodeProof("0x01", [pkCommitment]); - await expect( - bfvPkVerifier.verify.staticCall(proof, foldProof), - ).to.be.revert(ethers); + const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + expect(result).to.equal(false); }); }); describe("success", function () { - it("returns publicInputs[last] when circuit verifier returns true", async function () { + it("returns true when commitment matches and circuit verifier passes", async function () { const { bfvPkVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); await mockCircuit.setReturnValue(true); - const commitment = ethers.keccak256("0xabcd"); + const pkCommitment = ethers.keccak256("0xabcd"); const proof = encodeProof("0x0102", [ "0x" + "00".repeat(32), "0x" + "00".repeat(32), - commitment, + pkCommitment, ]); - const result = await bfvPkVerifier.verify.staticCall(proof, EMPTY_FOLD); - expect(result).to.equal(commitment); + const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + expect(result).to.equal(true); }); - it("returns commitment with single public input", async function () { + it("returns true with single matching public input", async function () { const { bfvPkVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); await mockCircuit.setReturnValue(true); - const commitment = ethers.id("committee-pk"); - const proof = encodeProof("0x", [commitment]); - - const result = await bfvPkVerifier.verify.staticCall(proof, EMPTY_FOLD); - expect(result).to.equal(commitment); - }); - - it("returns commitment when fold proof is present and fold verifies", async function () { - const { bfvPkVerifier, mockCircuit, mockFold } = await loadFixture( - deployWithMockCircuit, - ); - await mockCircuit.setReturnValue(true); - await mockFold.setReturnValue(true); - - const commitment = ethers.keccak256("0xabcd"); - const proof = encodeProof("0x0102", [ - "0x" + "00".repeat(32), - "0x" + "00".repeat(32), - commitment, - ]); - const foldPi = ["0x" + "02".repeat(32)]; - const foldProof = encodeProof("0x99", foldPi); + const pkCommitment = ethers.id("committee-pk"); + const proof = encodeProof("0x", [pkCommitment]); - const result = await bfvPkVerifier.verify.staticCall(proof, foldProof); - expect(result).to.equal(commitment); + const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + expect(result).to.equal(true); }); }); }); diff --git a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts index 2d55af2125..0ac7414139 100644 --- a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts +++ b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts @@ -31,7 +31,7 @@ import { MockUSDC__factory as MockUSDCFactory, SlashingManager__factory as SlashingManagerFactory, } from "../../types"; -import { encodePkProof, signAndEncodeAttestation } from "../fixtures"; +import { signAndEncodeAttestation } from "../fixtures"; const { ethers, ignition, networkHelpers } = await network.connect(); const { loadFixture, time } = networkHelpers; @@ -444,9 +444,9 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; - const pkProof = encodePkProof(ethers.keccak256(publicKey)); + const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, nodes, publicKey, pkProof, "0x"); + await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); // Verify stage transitioned to KeyPublished (after publishCommittee which calls onKeyPublished) stage = await enclave.getE3Stage(0); @@ -489,10 +489,10 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; - const pkProof = encodePkProof(ethers.keccak256(publicKey)); + const pkCommitment = ethers.keccak256(publicKey); await expect( - registry.publishCommittee(0, nodes, publicKey, pkProof, "0x"), + registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"), ) .to.emit(enclave, "CommitteeFormed") .withArgs(0); @@ -755,8 +755,8 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; - const pkProof = encodePkProof(ethers.keccak256(publicKey)); - await registry.publishCommittee(0, nodes, publicKey, pkProof, "0x"); + const pkCommitment = ethers.keccak256(publicKey); + await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); // 2. Wait past compute deadline → mark as failed const e3 = await enclave.getE3(0); @@ -859,8 +859,8 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; - const pkProof = encodePkProof(ethers.keccak256(publicKey)); - await registry.publishCommittee(0, nodes, publicKey, pkProof, "0x"); + const pkCommitment = ethers.keccak256(publicKey); + await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); // 2. Fail via compute timeout const e3 = await enclave.getE3(0); @@ -1111,8 +1111,8 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; - const pkProof = encodePkProof(ethers.keccak256(publicKey)); - await registry.publishCommittee(0, nodes, publicKey, pkProof, "0x"); + const pkCommitment = ethers.keccak256(publicKey); + await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); stage = await enclave.getE3Stage(0); expect(stage).to.equal(3); // KeyPublished @@ -1190,8 +1190,8 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; - const pkProof = encodePkProof(ethers.keccak256(publicKey)); - await registry.publishCommittee(0, nodes, publicKey, pkProof, "0x"); + const pkCommitment = ethers.keccak256(publicKey); + await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); stage = await enclave.getE3Stage(0); expect(stage).to.equal(3); // KeyPublished @@ -1433,8 +1433,8 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; - const pkProof = encodePkProof(ethers.keccak256(publicKey)); - await registry.publishCommittee(0, nodes, publicKey, pkProof, "0x"); + const pkCommitment = ethers.keccak256(publicKey); + await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); expect(await enclave.getE3Stage(0)).to.equal(3); // KeyPublished @@ -1487,12 +1487,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { ); const plaintextOutput = "0x" + "cd".repeat(100); - await enclave.publishPlaintextOutput( - 0, - plaintextOutput, - proofBytes, - "0x", - ); + await enclave.publishPlaintextOutput(0, plaintextOutput, proofBytes); expect(await enclave.getE3Stage(0)).to.equal(5); // Complete // 4. Verify escrowed slashed funds were distributed @@ -1569,8 +1564,8 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; - const pkProof = encodePkProof(ethers.keccak256(publicKey)); - await registry.publishCommittee(0, nodes, publicKey, pkProof, "0x"); + const pkCommitment = ethers.keccak256(publicKey); + await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); expect(await enclave.getE3Stage(0)).to.equal(3); // KeyPublished @@ -1585,7 +1580,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { // 4. Publish plaintext output const plaintextOutput = "0x" + "cd".repeat(100); - await enclave.publishPlaintextOutput(0, plaintextOutput, proof, "0x"); + await enclave.publishPlaintextOutput(0, plaintextOutput, proof); expect(await enclave.getE3Stage(0)).to.equal(5); // Complete // Cannot mark completed E3 as failed @@ -1628,8 +1623,8 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await operator3.getAddress(), ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; - const pkProof = encodePkProof(ethers.keccak256(publicKey)); - await registry.publishCommittee(0, nodes, publicKey, pkProof, "0x"); + const pkCommitment = ethers.keccak256(publicKey); + await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); // Publish outputs const e3 = await enclave.getE3(0); @@ -1640,7 +1635,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await enclave.publishCiphertextOutput(0, ciphertextOutput, proof); const plaintextOutput = "0x" + "cd".repeat(100); - await enclave.publishPlaintextOutput(0, plaintextOutput, proof, "0x"); + await enclave.publishPlaintextOutput(0, plaintextOutput, proof); // Verify E3 is complete expect(await enclave.getE3Stage(0)).to.equal(5); // Complete diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 8b206bfe14..8535215348 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -27,7 +27,7 @@ import { } from "../types"; import type { Enclave } from "../types/contracts/Enclave"; import type { MockUSDC } from "../types/contracts/test/MockStableToken.sol/MockUSDC"; -import { encodePkProof, setupOperatorForSortition } from "./fixtures"; +import { setupOperatorForSortition } from "./fixtures"; const { ethers, ignition, networkHelpers } = await network.connect(); const { loadFixture, time, mine } = networkHelpers; @@ -69,20 +69,34 @@ describe("Enclave", function () { const data = "0xda7a"; const proof = "0x1337"; + /** ABI-encoded fake DKG proof for `MockPkVerifier` (last public input must equal `pkCommitment`). */ + const encodeMockDkgProof = (pkCommitment: string): string => + ethers.AbiCoder.defaultAbiCoder().encode( + ["bytes", "bytes32[]"], + ["0x", [pkCommitment]], + ); + const setupAndPublishCommittee = async ( registry: any, e3Id: number, nodes: string[], publicKey: string, operators: Signer[], + committeeProof: string = "0x", ): Promise => { for (const operator of operators) { await registry.connect(operator).submitTicket(e3Id, 1); } await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(e3Id); - const proof = encodePkProof(ethers.keccak256(publicKey)); - await registry.publishCommittee(e3Id, nodes, publicKey, proof, "0x"); + const pkCommitment = ethers.keccak256(publicKey); + await registry.publishCommittee( + e3Id, + nodes, + publicKey, + pkCommitment, + committeeProof, + ); }; // Helper function to approve USDC and make request @@ -1008,7 +1022,7 @@ describe("Enclave", function () { const { enclave } = await loadFixture(setup); const e3Id = 0; - await expect(enclave.publishPlaintextOutput(e3Id, data, "0x", "0x")) + await expect(enclave.publishPlaintextOutput(e3Id, data, "0x")) .to.be.revertedWithCustomError(enclave, "E3DoesNotExist") .withArgs(e3Id); }); @@ -1042,7 +1056,7 @@ describe("Enclave", function () { [operator1, operator2, operator3], ); await expect( - enclave.publishPlaintextOutput(e3Id, data, "0x", "0x"), + enclave.publishPlaintextOutput(e3Id, data, "0x"), ).to.be.revertedWithCustomError(enclave, "InvalidStage"); }); it("reverts if plaintextOutput has already been published", async function () { @@ -1075,9 +1089,9 @@ describe("Enclave", function () { ); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); - await enclave.publishPlaintextOutput(e3Id, data, proof, "0x"); + await enclave.publishPlaintextOutput(e3Id, data, proof); await expect( - enclave.publishPlaintextOutput(e3Id, data, proof, "0x"), + enclave.publishPlaintextOutput(e3Id, data, proof), ).to.be.revertedWithCustomError(enclave, "InvalidStage"); }); it("reverts if output is not valid", async function () { @@ -1098,6 +1112,7 @@ describe("Enclave", function () { proofAggregationEnabled: true, }); + const pkCommitment = ethers.keccak256(data); await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, @@ -1108,10 +1123,11 @@ describe("Enclave", function () { ], data, [operator1, operator2, operator3], + encodeMockDkgProof(pkCommitment), ); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); - await expect(enclave.publishPlaintextOutput(e3Id, data, "0x", "0x")) + await expect(enclave.publishPlaintextOutput(e3Id, data, "0xdeadbeef")) .to.be.revertedWithCustomError(enclave, "InvalidOutput") .withArgs(data); }); @@ -1145,7 +1161,7 @@ describe("Enclave", function () { ); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); - expect(await enclave.publishPlaintextOutput(e3Id, data, proof, "0x")); + expect(await enclave.publishPlaintextOutput(e3Id, data, proof)); const e3 = await enclave.getE3(e3Id); expect(e3.plaintextOutput).to.equal(data); @@ -1181,12 +1197,7 @@ describe("Enclave", function () { await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); expect( - await enclave.publishPlaintextOutput.staticCall( - e3Id, - data, - proof, - "0x", - ), + await enclave.publishPlaintextOutput.staticCall(e3Id, data, proof), ).to.equal(true); }); it("emits PlaintextOutputPublished event", async function () { @@ -1219,9 +1230,7 @@ describe("Enclave", function () { ); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); - await expect( - await enclave.publishPlaintextOutput(e3Id, data, proof, "0x"), - ) + await expect(await enclave.publishPlaintextOutput(e3Id, data, proof)) .to.emit(enclave, "PlaintextOutputPublished") .withArgs(e3Id, data, proof); }); diff --git a/packages/enclave-contracts/test/Pricing/Pricing.spec.ts b/packages/enclave-contracts/test/Pricing/Pricing.spec.ts index 9b4960a5c1..73908a950e 100644 --- a/packages/enclave-contracts/test/Pricing/Pricing.spec.ts +++ b/packages/enclave-contracts/test/Pricing/Pricing.spec.ts @@ -25,7 +25,6 @@ import { Enclave__factory as EnclaveFactory, MockUSDC__factory as MockUSDCFactory, } from "../../types"; -import { encodePkProof } from "../fixtures"; const { ethers, ignition, networkHelpers } = await network.connect(); const { loadFixture, time, mine } = networkHelpers; @@ -141,8 +140,8 @@ describe("E3 Pricing", function () { } await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(e3Id); - const proof = encodePkProof(ethers.keccak256(publicKey)); - await registry.publishCommittee(e3Id, nodes, publicKey, proof, "0x"); + const pkCommitment = ethers.keccak256(publicKey); + await registry.publishCommittee(e3Id, nodes, publicKey, pkCommitment, "0x"); }; const setup = async () => { @@ -636,7 +635,7 @@ describe("E3 Pricing", function () { const op3Before = await usdcToken.balanceOf(nodes[2]); // Publish plaintext (triggers _distributeRewards) - await enclave.publishPlaintextOutput(e3Id, data, proof, proof); + await enclave.publishPlaintextOutput(e3Id, data, proof); const op1After = await usdcToken.balanceOf(nodes[0]); const op2After = await usdcToken.balanceOf(nodes[1]); @@ -718,7 +717,7 @@ describe("E3 Pricing", function () { const op2Before = await usdcToken.balanceOf(nodes[1]); const op3Before = await usdcToken.balanceOf(nodes[2]); - await enclave.publishPlaintextOutput(e3Id, data, proof, proof); + await enclave.publishPlaintextOutput(e3Id, data, proof); const treasuryAfter = await usdcToken.balanceOf(treasuryAddr); const op1After = await usdcToken.balanceOf(nodes[0]); diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts index 5e073d63f8..ea53846141 100644 --- a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts @@ -23,7 +23,7 @@ import { CiphernodeRegistryOwnable__factory as CiphernodeRegistryFactory, Enclave__factory as EnclaveFactory, } from "../../types"; -import { encodePkProof, setupOperatorForSortition } from "../fixtures"; +import { setupOperatorForSortition } from "../fixtures"; const AddressOne = "0x0000000000000000000000000000000000000001"; const AddressTwo = "0x0000000000000000000000000000000000000002"; @@ -33,7 +33,6 @@ const { loadFixture } = networkHelpers; const data = "0xda7a"; const dataHash = ethers.id(data); -const c5Proof = encodePkProof(dataHash); const SORTITION_SUBMISSION_WINDOW = 3; describe("CiphernodeRegistryOwnable", function () { @@ -435,7 +434,7 @@ describe("CiphernodeRegistryOwnable", function () { await operator3.getAddress(), ], data, - c5Proof, + dataHash, "0x", ), ) @@ -448,7 +447,8 @@ describe("CiphernodeRegistryOwnable", function () { await operator3.getAddress(), ], data, - c5Proof, + dataHash, + "0x", ); }); it("stores the public key of the committee", async function () { @@ -482,7 +482,7 @@ describe("CiphernodeRegistryOwnable", function () { await operator3.getAddress(), ], data, - c5Proof, + dataHash, "0x", ); expect(await registry.committeePublicKey(0)).to.equal(dataHash); @@ -520,7 +520,7 @@ describe("CiphernodeRegistryOwnable", function () { await operator3.getAddress(), ], data, - c5Proof, + dataHash, "0x", ), ) @@ -533,7 +533,8 @@ describe("CiphernodeRegistryOwnable", function () { await operator3.getAddress(), ], data, - c5Proof, + dataHash, + "0x", ); }); }); @@ -701,7 +702,7 @@ describe("CiphernodeRegistryOwnable", function () { await operator3.getAddress(), ], data, - c5Proof, + dataHash, "0x", ); expect(await registry.committeePublicKey(e3Id)).to.equal(dataHash); diff --git a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts index b88a3d3409..bc4bf24e99 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -40,7 +40,7 @@ import { MockUSDC__factory as MockUSDCFactory, SlashingManager__factory as SlashingManagerFactory, } from "../../types"; -import { encodePkProof, signAndEncodeAttestation } from "../fixtures"; +import { signAndEncodeAttestation } from "../fixtures"; const { ethers, ignition, networkHelpers } = await network.connect(); const { loadFixture, time } = networkHelpers; @@ -344,8 +344,14 @@ describe("Committee Expulsion & Fault Tolerance", function () { const nodes = await Promise.all(operators.map((op) => op.getAddress())); const publicKey = ethers.toUtf8Bytes("fake-public-key"); - const proof = encodePkProof(ethers.keccak256(publicKey)); - await registry.publishCommittee(e3Id, nodes, publicKey, proof, "0x"); + const pkCommitment = ethers.keccak256(publicKey); + await registry.publishCommittee( + e3Id, + nodes, + publicKey, + pkCommitment, + "0x", + ); } // ── Return ───────────────────────────────────────────────────────────────── diff --git a/packages/enclave-contracts/test/fixtures/index.ts b/packages/enclave-contracts/test/fixtures/index.ts index 8f174d8138..92e1403f1f 100644 --- a/packages/enclave-contracts/test/fixtures/index.ts +++ b/packages/enclave-contracts/test/fixtures/index.ts @@ -5,5 +5,4 @@ // or FITNESS FOR A PARTICULAR PURPOSE. export { VOTE_TYPEHASH, signAndEncodeAttestation } from "./attestation"; -export { encodePkProof } from "./pkProof"; export { setupOperatorForSortition } from "./operators"; diff --git a/packages/enclave-contracts/test/fixtures/pkProof.ts b/packages/enclave-contracts/test/fixtures/pkProof.ts deleted file mode 100644 index b46a66e06e..0000000000 --- a/packages/enclave-contracts/test/fixtures/pkProof.ts +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. -import { network } from "hardhat"; - -const { ethers } = await network.connect(); - -/** - * Encodes a fake pk_aggregation proof for testing when no real proof is available. - * Format: abi.encode(bytes rawProof, bytes32[] publicInputs) with commitment as last input. - * @param commitment The aggregate public key commitment (bytes32) as last public input. - * @returns ABI-encoded proof bytes. - */ -export function encodePkProof(commitment: string): string { - const abiCoder = ethers.AbiCoder.defaultAbiCoder(); - return abiCoder.encode(["bytes", "bytes32[]"], ["0x", [commitment]]); -} diff --git a/scripts/README.md b/scripts/README.md index f1c15ef6b5..68bd8debfa 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -278,8 +278,8 @@ Automates the full pipeline from Noir circuits to on-chain Solidity verifiers: 3. **Generates verification keys** using `bb write_vk -t evm` 4. **Generates Solidity verifiers** using `bb write_solidity_verifier` 5. **Post-processes** the generated Solidity: - - Renames contract from `HonkVerifier` to descriptive name (e.g., - `ThresholdPkAggregationVerifier`, `ThresholdPkGenerationVerifier`) + - Renames contract from `HonkVerifier` to descriptive name (e.g., `DkgAggregatorVerifier`, + `DecryptionAggregatorVerifier`) - Replaces Apache-2.0 license header with LGPL-3.0-only 6. **Outputs** to `packages/enclave-contracts/contracts/verifiers/bfv/honk/` @@ -304,13 +304,12 @@ The `generate:verifiers` script in package.json passes `--circuits` with the on- ``` 🔮 Generating Solidity verifiers from Noir circuits... - Found 4 circuit(s) + Found 2 circuit(s) - ✓ threshold/pk_aggregation → ThresholdPkAggregationVerifier.sol - ✓ threshold/decrypted_shares_aggregation → ThresholdDecryptedSharesAggregationVerifier.sol - ✓ recursive_aggregation/fold → RecursiveAggregationFoldVerifier.sol + ✓ recursive_aggregation/dkg_aggregator → DkgAggregatorVerifier.sol + ✓ recursive_aggregation/decryption_aggregator → DecryptionAggregatorVerifier.sol -✅ Generated 4 Solidity verifier(s) in: +✅ Generated 2 Solidity verifier(s) in: packages/enclave-contracts/contracts/verifiers/bfv/honk/ ``` diff --git a/scripts/build-circuits.ts b/scripts/build-circuits.ts index 25c8251926..cc1ef1cb28 100644 --- a/scripts/build-circuits.ts +++ b/scripts/build-circuits.ts @@ -20,13 +20,6 @@ import { type CircuitPreset, } from './circuit-constants' -/** share_computation wrapper is shared by sk_share_computation and e_sm_share_computation. */ -const SHARE_COMP_WRAPPER = { - path: ['recursive_aggregation', 'wrapper', 'dkg'] as const, - aliases: ['sk_share_computation', 'e_sm_share_computation'] as const, - variants: [CIRCUIT_VARIANTS.DEFAULT, CIRCUIT_VARIANTS.RECURSIVE] as const, -} - interface CircuitInfo { name: string group: CircuitGroup @@ -362,8 +355,15 @@ class NoirCircuitBuilder { return result } - private isWrapper(circuit: CircuitInfo): boolean { - return circuit.name.startsWith('wrapper/') + private isFoldOrAggregation(circuit: CircuitInfo): boolean { + return circuit.group === CIRCUIT_GROUPS.AGGREGATION + } + + /** Aggregation circuits that are also published on-chain as EVM verifiable proofs. */ + private isAggregationWithEvmOnChain(circuit: CircuitInfo): boolean { + if (circuit.group !== CIRCUIT_GROUPS.AGGREGATION) return false + const name = basename(circuit.path) + return name === 'dkg_aggregator' || name === 'decryption_aggregator' } private generateVk( @@ -387,7 +387,7 @@ class NoirCircuitBuilder { vkNoir: null as string | null, vkNoirHash: null as string | null, } - const isWrapper = this.isWrapper(circuit) + const isFoldOrAggregation = this.isFoldOrAggregation(circuit) const runWriteVk = (verifierTarget: string, vkOut: string, vkHashOut: string): boolean => { try { @@ -420,36 +420,36 @@ class NoirCircuitBuilder { const vkNoirFile = join(targetDir, `${packageName}.vk_noir`) const vkNoirHashFile = join(targetDir, `${packageName}.vk_noir_hash`) - if (!isWrapper) { - // evm VK: for on-chain Solidity verification - if (!runWriteVk('evm', vkFile, vkHashFile)) { - throw new Error(`VK generation failed for ${packageName} (evm)`) - } - result.vk = existsSync(vkFile) ? vkFile : null - result.vkHash = existsSync(vkHashFile) ? vkHashFile : null - - // noir-recursive-no-zk VK: for wrapper/fold output verification (Default variant) + if (isFoldOrAggregation) { + // Aggregation fold circuits: Default variant (noir-recursive-no-zk) for witness generation. if (!runWriteVk('noir-recursive-no-zk', vkRecursiveFile, vkRecursiveHashFile)) { throw new Error(`VK generation failed for ${packageName} (noir-recursive-no-zk)`) } result.vkRecursive = existsSync(vkRecursiveFile) ? vkRecursiveFile : null result.vkRecursiveHash = existsSync(vkRecursiveHashFile) ? vkRecursiveHashFile : null - // noir-recursive VK: for inner/base proofs embedded in wrapper inputs (Recursive variant) - if (!runWriteVk('noir-recursive', vkNoirFile, vkNoirHashFile)) { - throw new Error(`VK generation failed for ${packageName} (noir-recursive)`) + // DKG / decryption aggregators are additionally proven with `-t evm` for on-chain verification. + if (this.isAggregationWithEvmOnChain(circuit)) { + if (!runWriteVk('evm', vkFile, vkHashFile)) { + throw new Error(`VK generation failed for ${packageName} (evm)`) + } + result.vk = existsSync(vkFile) ? vkFile : null + result.vkHash = existsSync(vkHashFile) ? vkHashFile : null } - result.vkNoir = existsSync(vkNoirFile) ? vkNoirFile : null - result.vkNoirHash = existsSync(vkNoirHashFile) ? vkNoirHashFile : null } else { - // Wrapper circuits: noir-recursive-no-zk (Default variant) + // Base DKG/threshold circuits: evm + noir-recursive-no-zk + noir-recursive + if (!runWriteVk('evm', vkFile, vkHashFile)) { + throw new Error(`VK generation failed for ${packageName} (evm)`) + } + result.vk = existsSync(vkFile) ? vkFile : null + result.vkHash = existsSync(vkHashFile) ? vkHashFile : null + if (!runWriteVk('noir-recursive-no-zk', vkRecursiveFile, vkRecursiveHashFile)) { throw new Error(`VK generation failed for ${packageName} (noir-recursive-no-zk)`) } result.vkRecursive = existsSync(vkRecursiveFile) ? vkRecursiveFile : null result.vkRecursiveHash = existsSync(vkRecursiveHashFile) ? vkRecursiveHashFile : null - // noir-recursive VK: needed if wrapper/fold proofs are embedded in further recursive aggregation if (!runWriteVk('noir-recursive', vkNoirFile, vkNoirHashFile)) { throw new Error(`VK generation failed for ${packageName} (noir-recursive)`) } @@ -550,27 +550,6 @@ class NoirCircuitBuilder { } } - // share_computation wrapper aliases - const wrapperPath = SHARE_COMP_WRAPPER.path.join('/') - const presets = Array.from(new Set(compiled.map((c) => c.preset))) - for (const preset of presets) { - for (const variant of SHARE_COMP_WRAPPER.variants) { - const shareCompPrefix = `${preset}/${variant}/${wrapperPath}/share_computation` - const basePrefix = `${preset}/${variant}/${wrapperPath}` - for (const alias of SHARE_COMP_WRAPPER.aliases) { - for (const suffix of ['.json', '.vk', '.vk_hash']) { - const srcKey = `${shareCompPrefix}/share_computation${suffix}` - const srcHash = checksums[srcKey] - if (srcHash) { - const aliasKey = `${basePrefix}/${alias}/${alias}${suffix}` - checksums[aliasKey] = srcHash - lines.push(`${srcHash} ${aliasKey}`) - } - } - } - } - } - const outputDir = this.options.outputDir! writeFileSync(join(outputDir, 'SHA256SUMS'), lines.join('\n') + '\n') writeFileSync( @@ -594,7 +573,7 @@ class NoirCircuitBuilder { if (c.artifacts.vkHash) copyFileSync(c.artifacts.vkHash, join(evmDir, basename(c.artifacts.vkHash))) } - // Copy to default/ variant: .json + noir-recursive-no-zk .vk (wrapper/fold proofs) + // Copy to default/ variant: .json + noir-recursive-no-zk .vk (aggregation fold proofs) const defaultDir = join(outputDir, CIRCUIT_VARIANTS.DEFAULT, c.group, c.name) mkdirSync(defaultDir, { recursive: true }) if (c.artifacts.json) copyFileSync(c.artifacts.json, join(defaultDir, basename(c.artifacts.json))) @@ -617,23 +596,6 @@ class NoirCircuitBuilder { } } - // Share_computation wrapper aliases (source includes circuit group per SHARE_COMP_WRAPPER.path) - for (const variant of SHARE_COMP_WRAPPER.variants) { - const shareCompSrc = join(outputDir, variant, ...SHARE_COMP_WRAPPER.path, 'share_computation') - const hasJson = existsSync(join(shareCompSrc, 'share_computation.json')) - const hasVk = existsSync(join(shareCompSrc, 'share_computation.vk')) - const hasVkHash = existsSync(join(shareCompSrc, 'share_computation.vk_hash')) - if (hasJson || hasVk || hasVkHash) { - for (const alias of SHARE_COMP_WRAPPER.aliases) { - const destDir = join(outputDir, variant, ...SHARE_COMP_WRAPPER.path, alias) - mkdirSync(destDir, { recursive: true }) - if (hasJson) copyFileSync(join(shareCompSrc, 'share_computation.json'), join(destDir, `${alias}.json`)) - if (hasVk) copyFileSync(join(shareCompSrc, 'share_computation.vk'), join(destDir, `${alias}.vk`)) - if (hasVkHash) copyFileSync(join(shareCompSrc, 'share_computation.vk_hash'), join(destDir, `${alias}.vk_hash`)) - } - } - } - return outputDir } diff --git a/scripts/generate-verifiers.ts b/scripts/generate-verifiers.ts index 3c5e75b5e2..bed14998a0 100644 --- a/scripts/generate-verifiers.ts +++ b/scripts/generate-verifiers.ts @@ -103,6 +103,21 @@ class VerifierGenerator { } mkdirSync(this.verifierDir, { recursive: true }) + // Pre-flight: two circuits with the same leaf name would silently overwrite each other's .sol. + const seen = new Map() + for (const circuit of circuits) { + const contractFile = `${this.toContractName(circuit.name)}.sol` + const prior = seen.get(contractFile) + if (prior) { + throw new Error( + `Duplicate Solidity verifier filename ${contractFile} for circuits ` + + `${prior} and ${circuit.group}/${circuit.name}; rename one of the circuits or ` + + `extend toContractName to include the group prefix.`, + ) + } + seen.set(contractFile, `${circuit.group}/${circuit.name}`) + } + const generated: string[] = [] const errors: string[] = [] @@ -192,7 +207,7 @@ class VerifierGenerator { } // 4. Post-process: rename contract, add license header, copy to output - const contractName = this.toContractName(group, name) + const contractName = this.toContractName(name) const outputFileName = `${contractName}.sol` const outputPath = join(this.verifierDir, outputFileName) @@ -292,18 +307,16 @@ class VerifierGenerator { } /** - * Convert group/name to a PascalCase Solidity contract name. - * e.g. (threshold, pk_aggregation) → ThresholdPkAggregationVerifier - * (threshold, pk_generation) → ThresholdPkGenerationVerifier - * (recursive_aggregation, fold) → RecursiveAggregationFoldVerifier + * Convert circuit folder name to a PascalCase Solidity contract name. + * e.g. dkg_aggregator → DkgAggregatorVerifier, decryption_aggregator → DecryptionAggregatorVerifier */ - private toContractName(group: CircuitGroup, name: string): string { + private toContractName(name: string): string { const pascal = (s: string) => s .split(/[_-]+/) .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()) .join('') - return `${pascal(group)}${pascal(name)}Verifier` + return `${pascal(name)}Verifier` } /** @@ -399,8 +412,8 @@ Options: -h, --help Show this help message Examples: - pnpm generate:verifiers --circuits pk,pk_aggregation,decrypted_shares_aggregation,fold - pnpm generate:verifiers --circuits pk --clean + pnpm generate:verifiers --circuits dkg_aggregator,decryption_aggregator + pnpm generate:verifiers --circuits dkg_aggregator --clean `) } diff --git a/scripts/lint-circuits.sh b/scripts/lint-circuits.sh index 481128f50e..354d7a8ecf 100755 --- a/scripts/lint-circuits.sh +++ b/scripts/lint-circuits.sh @@ -11,7 +11,24 @@ fi cd circuits # Directories to check -DIRS=("lib" "bin/config" "bin/recursive_aggregation/fold" "bin/recursive_aggregation/wrapper/dkg" "bin/recursive_aggregation/wrapper/threshold" "bin/dkg" "bin/threshold") +DIRS=( + "lib" + "bin/config" + "bin/recursive_aggregation/c2ab_fold" + "bin/recursive_aggregation/c3_fold" + "bin/recursive_aggregation/c3_fold_kernel" + "bin/recursive_aggregation/c6_fold" + "bin/recursive_aggregation/c6_fold_kernel" + "bin/recursive_aggregation/c3ab_fold" + "bin/recursive_aggregation/c4ab_fold" + "bin/recursive_aggregation/node_fold" + "bin/recursive_aggregation/nodes_fold" + "bin/recursive_aggregation/nodes_fold_kernel" + "bin/recursive_aggregation/dkg_aggregator" + "bin/recursive_aggregation/decryption_aggregator" + "bin/dkg" + "bin/threshold" +) for dir in "${DIRS[@]}"; do if [ ! -d "$dir" ]; then diff --git a/templates/default/deployed_contracts.json b/templates/default/deployed_contracts.json index d206e0b287..2c05d19759 100644 --- a/templates/default/deployed_contracts.json +++ b/templates/default/deployed_contracts.json @@ -1,21 +1,21 @@ { "localhost": { "PoseidonT3": { - "blockNumber": 6, + "blockNumber": 17, "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "MockUSDC": { "constructorArgs": { "initialSupply": "1000000" }, - "blockNumber": 7, + "blockNumber": 18, "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" }, "EnclaveToken": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 8, + "blockNumber": 19, "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" }, "EnclaveTicketToken": { @@ -24,14 +24,14 @@ "registry": "0x0000000000000000000000000000000000000001", "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 10, + "blockNumber": 21, "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" }, "SlashingManager": { "constructorArgs": { "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 11, + "blockNumber": 22, "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" }, "CiphernodeRegistryOwnable": { @@ -46,7 +46,7 @@ "proxyAdminAddress": "0x9bd03768a7DCc129555dE410FF8E85528A4F88b5", "implementationAddress": "0x0165878A594ca255338adfa4d48449f69242Eb8F" }, - "blockNumber": 12, + "blockNumber": 23, "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" }, "BondingRegistry": { @@ -68,7 +68,7 @@ "proxyAdminAddress": "0x8aCd85898458400f7Db866d53FCFF6f0D49741FF", "implementationAddress": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" }, - "blockNumber": 13, + "blockNumber": 24, "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" }, "Enclave": { @@ -88,7 +88,7 @@ "proxyAdminAddress": "0x8dAF17A20c9DBA35f005b6324F493785D239719d", "implementationAddress": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" }, - "blockNumber": 16, + "blockNumber": 27, "address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, "E3RefundManager": { @@ -104,56 +104,52 @@ "proxyAdminAddress": "0x32467b43BFa67273FC7dDda0999Ee9A12F2AaA08", "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" }, - "blockNumber": 18, + "blockNumber": 29, "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" }, "MockComputeProvider": { - "blockNumber": 20, + "blockNumber": 31, "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" }, "MockDecryptionVerifier": { - "blockNumber": 21, + "blockNumber": 32, "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9" }, "MockPkVerifier": { - "blockNumber": 22, + "blockNumber": 33, "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" }, "MockE3Program": { - "blockNumber": 23, + "blockNumber": 34, "address": "0x851356ae760d987E095750cCeb3bC6014560891C" }, "ZKTranscriptLib": { - "blockNumber": 25, + "blockNumber": 36, "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778" }, - "RecursiveAggregationFoldVerifier": { - "blockNumber": 26, + "DecryptionAggregatorVerifier": { + "blockNumber": 37, "address": "0x998abeb3E57409262aE5b751f60747921B33613E" }, - "ThresholdDecryptedSharesAggregationVerifier": { - "blockNumber": 27, + "DkgAggregatorVerifier": { + "blockNumber": 38, "address": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49" }, - "ThresholdPkAggregationVerifier": { - "blockNumber": 28, - "address": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528" - }, "BfvDecryptionVerifier": { - "blockNumber": 29, - "address": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf" + "blockNumber": 39, + "address": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528" }, "BfvPkVerifier": { - "blockNumber": 31, - "address": "0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf" + "blockNumber": 41, + "address": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF" }, "ImageID": { - "address": "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570", - "blockNumber": 34 + "address": "0x5eb3Bc0a489C5A8288765d2336659EbCA68FCd00", + "blockNumber": 44 }, "MyProgram": { - "address": "0x809d550fca64d94Bd9F66E60752A544199cfAC3D", - "blockNumber": 36 + "address": "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570", + "blockNumber": 46 } } } \ No newline at end of file diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index 4b1cdf9106..0b0e321742 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -4,22 +4,22 @@ chains: contracts: enclave: address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" - deploy_block: 16 + deploy_block: 27 ciphernode_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" - deploy_block: 12 + deploy_block: 23 bonding_registry: address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" - deploy_block: 13 + deploy_block: 24 slashing_manager: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" deploy_block: 11 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - deploy_block: 7 + deploy_block: 18 e3_program: - address: "0x809d550fca64d94Bd9F66E60752A544199cfAC3D" - deploy_block: 36 + address: "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570" + deploy_block: 46 program: dev: true nodes: diff --git a/templates/default/hardhat.config.ts b/templates/default/hardhat.config.ts index 19474e187d..7e4816eeb8 100644 --- a/templates/default/hardhat.config.ts +++ b/templates/default/hardhat.config.ts @@ -115,9 +115,8 @@ const config: HardhatUserConfig = { '@enclave-e3/contracts/contracts/token/EnclaveTicketToken.sol', '@enclave-e3/contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol', '@enclave-e3/contracts/contracts/verifiers/bfv/BfvPkVerifier.sol', - '@enclave-e3/contracts/contracts/verifiers/bfv/honk/RecursiveAggregationFoldVerifier.sol', - '@enclave-e3/contracts/contracts/verifiers/bfv/honk/ThresholdDecryptedSharesAggregationVerifier.sol', - '@enclave-e3/contracts/contracts/verifiers/bfv/honk/ThresholdPkAggregationVerifier.sol', + '@enclave-e3/contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol', + '@enclave-e3/contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol', '@enclave-e3/contracts/contracts/test/MockCiphernodeRegistry.sol', '@enclave-e3/contracts/contracts/test/MockComputeProvider.sol', '@enclave-e3/contracts/contracts/test/MockDecryptionVerifier.sol', diff --git a/tests/integration/enclave.config.yaml b/tests/integration/enclave.config.yaml index 142f2ca87b..c4d778416e 100644 --- a/tests/integration/enclave.config.yaml +++ b/tests/integration/enclave.config.yaml @@ -3,7 +3,7 @@ chains: rpc_url: "ws://localhost:8545" contracts: e3_program: - address: "0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB" + address: "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570" deploy_block: 1 # Set to actual deploy block enclave: address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e"