diff --git a/circuits/README.md b/circuits/README.md new file mode 100644 index 0000000000..5c1dc4205d --- /dev/null +++ b/circuits/README.md @@ -0,0 +1,114 @@ +# Circuits + +This directory holds the **Noir** implementation of Interfold’s zero-knowledge circuits: distributed +key generation and encrypted share handling (**BFV**), threshold key generation, user encryption, +and threshold decryption (**TrBFV**), together with recursive proof aggregation. + +The Noir sources and tests in this tree are authoritative for constraints and public inputs. +Everything else—docs, diagrams, comments—is there to help you navigate; when in doubt, trust the +code. + +```text +circuits/ +├── lib/ +│ └── src/ +│ ├── configs/ # BFV / CRT parameter presets +│ ├── core/dkg/ # Shared logic: C0, C2–C4 +│ ├── core/threshold/ # Shared logic: C1, C5, P3, C6, C7 +│ └── math/ # Polynomials, SAFE, commitments, modular arithmetic +├── bin/ +│ ├── config/ # Deployment-time consistency checks on presets +│ ├── dkg/ # DKG packages and C2 proof pipeline +│ ├── threshold/ # TrBFV, user encryption, threshold decryption +│ └── recursive_aggregation/ +│ ├── fold/ +│ └── wrapper/ +│ ├── dkg/ +│ └── threshold/ +└── benchmarks/ +``` + +The shared library is the Nargo package **`lib`** (`lib/Nargo.toml`). All packages under `bin/` +depend on it; module structure is documented in [`lib/src/README.md`](lib/src/README.md). + +Packages under `bin/` with a `Nargo.toml` are build targets. Directory names align with the +**`CircuitName`** enum in `crates/events` via `CircuitName::group()` and `CircuitName::dir_path()`. +Workspace manifests also exist at `dkg/` and `threshold/` for grouped builds. + +## Circuit package index + +The tables below map **`circuits/bin/` paths** to **circuit labels** (C0–C7) and **`CircuitName`** +values used in Rust. Phases **P1–P4** are a product-level grouping of the same protocol steps; for +how phases, commitments, and circuit IDs line up end to end, read +[Cryptography](https://docs.theinterfold.com/cryptography) (source: +[`docs/pages/cryptography.mdx`](../docs/pages/cryptography.mdx)). + +**C2** is implemented as a **pipeline** of packages (base, chunk, batch, final `share_computation`), +not a single crate. + +### DKG (`bin/dkg/`) + +| Path | ID | `CircuitName` | Role | +| ------------------------------- | -------- | ---------------------------- | --------------------------------------------- | +| `pk` | C0 | `PkBfv` | Commit to individual BFV public key | +| `sk_share_computation_base` | C2 inner | `SkShareComputationBase` | Shamir shares (`y`) for secret contribution | +| `e_sm_share_computation_base` | C2 inner | `ESmShareComputationBase` | Shamir shares (`y`) for smudging noise | +| `share_computation_chunk` | C2 inner | `ShareComputationChunk` | Reed–Solomon parity on a coefficient slice | +| `share_computation_chunk_batch` | C2 inner | `ShareComputationChunkBatch` | Binds base proof to a batch of chunk proofs | +| `share_computation` | **C2** | `ShareComputation` | Final C2 step; aggregates inner proofs | +| `share_encryption` | C3 | `ShareEncryption` | BFV encryption of shares under recipient keys | +| `share_decryption` | C4 | `DkgShareDecryption` | Decrypt shares; aggregate; commitments for P4 | + +### Threshold (`bin/threshold/`) + +| Path | ID | `CircuitName` | Role | +| ------------------------------ | ---------- | ---------------------------- | ------------------------------------------------- | +| `pk_generation` | C1 | `PkGeneration` | Threshold public-key contribution | +| `pk_aggregation` | C5 | `PkAggregation` | Aggregate contributions into threshold public key | +| `user_data_encryption_ct0` | P3 | — | User ciphertext (first leg) | +| `user_data_encryption_ct1` | P3 | — | User ciphertext (second leg) | +| `user_data_encryption` | P3 wrapper | — | Wrapper: ct0, ct1, shared randomness | +| `share_decryption` | C6 | `ThresholdShareDecryption` | Partial decryption share | +| `decrypted_shares_aggregation` | C7 | `DecryptedSharesAggregation` | Combine shares; CRT; decode | + +### Recursive aggregation (`bin/recursive_aggregation/`) + +| Path | `CircuitName` | Role | +| --------------------- | ------------- | --------------------------------------------------------- | +| `fold` | `Fold` | Fold two wrapper outputs | +| `wrapper/dkg/*` | — | Verifies inner DKG proofs; compresses public inputs | +| `wrapper/threshold/*` | — | Verifies inner threshold proofs; compresses public inputs | + +Wrapper parameters are documented in +[`wrapper/README.md`](bin/recursive_aggregation/wrapper/README.md). + +### Configuration + +| Path | Role | +| -------- | ----------------------------------------------------------------------- | +| `config` | Validates secure preset constants (CRT moduli, bounds, parity matrices) | + +## Build and test + +From the repository root: + +```bash +pnpm tsx scripts/build-circuits.ts # compile circuits, verification keys, artifacts +./scripts/lint-circuits.sh # nargo fmt --check; nargo check (skipped if nargo absent) +./scripts/test-circuits.sh # unit tests in circuits/lib +``` + +Pin **nargo** and **bb** to the versions in `crates/zk-prover` and `versions.json`. For local work, +**`enclave noir setup`** installs a toolchain that lines up with the prover and the artifacts CI +produces. Install options and CLI flags are on the +[Noir Circuits](https://docs.theinterfold.com/noir-circuits) page +([`docs/pages/noir-circuits.mdx`](../docs/pages/noir-circuits.mdx)). + +## Related documentation + +| Topic | Location | +| ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | +| Cryptographic model (PV-TBFV, phases P1–P4, circuit identifiers C0–C7) | [Cryptography](https://docs.theinterfold.com/cryptography) · [source](../docs/pages/cryptography.mdx) | +| Toolchain, repository layout, `enclave noir`, compilation | [Noir Circuits](https://docs.theinterfold.com/noir-circuits) · [source](../docs/pages/noir-circuits.mdx) | +| Rust types (`ProofType`, `CircuitName`) | [`signed_proof.rs`](../crates/events/src/enclave_event/signed_proof.rs) · [`proof.rs`](../crates/events/src/enclave_event/proof.rs) | +| Protocol execution (actors, events, proof ordering) | [`agent/flow-trace/04_DKG_AND_COMPUTATION.md`](../agent/flow-trace/04_DKG_AND_COMPUTATION.md) | diff --git a/circuits/benchmarks/README.md b/circuits/benchmarks/README.md index 398bf97d23..2fa47fddab 100644 --- a/circuits/benchmarks/README.md +++ b/circuits/benchmarks/README.md @@ -1,62 +1,20 @@ -# Circuit benchmarks +# Benchmarks -Benchmark the ZK circuits. Configuration is in `config.json` (circuit list, mode, oracles, metrics). +Scripts to compile and time Nargo packages listed in `config.json` (`results_*/report.md`). -## How to run +| | | +| --------------------- | --------------------------------------------------- | +| **Circuits overview** | [README](../README.md) | +| **Docs** | [Noir Circuits](../../docs/pages/noir-circuits.mdx) | -From the **benchmarks** directory: +## Run -```bash -./run_benchmarks.sh [options] -``` - -Or from **scripts**: - -```bash -./scripts/run_benchmarks.sh [options] -``` - -Both use `config.json` in the benchmarks directory by default. - -### Options - -| Option | Description | -| --------------------------- | ----------------------------------------------------------------------------------------------------------- | -| `--mode insecure \| secure` | Run in insecure (default) or secure mode. Overrides `config.json`’s `mode`. | -| `--circuit ` | Run only this circuit (e.g. `dkg/pk` or `config`). If not in `config.json`, runs anyway if the path exists. | -| `--config ` | Use a different config file instead of `config.json`. | -| `--skip-compile` | Reuse existing build artifacts; skip compilation. | -| `--clean` | Remove circuit `target/` directories after the run. | - -### Examples +From this directory: ```bash -# Default: insecure mode, all circuits from config (config circuit is skipped) ./run_benchmarks.sh - -# Secure mode (includes the config circuit) -./run_benchmarks.sh --mode secure - -# Single circuit -./run_benchmarks.sh --circuit threshold/pk_generation -./run_benchmarks.sh --mode secure --circuit config - -# Re-run without recompiling +./run_benchmarks.sh --mode secure --circuit dkg/pk ./run_benchmarks.sh --skip-compile ``` -### Results - -Output goes under `results_/` (e.g. `results_insecure/`, `results_secure/`). A Markdown report -is written to `results_/report.md`. Raw JSON is kept in `results_/raw/` so that a run -with `--circuit` only overwrites that circuit’s file and the report is regenerated from all data -(existing + updated). View the report with `cat results_/report.md` or -`open results_/report.md` (macOS). - -## Secure-only circuits - -The **config** circuit (validates secure configs only) is listed in `config.json` with -`"modes": ["secure"]` so it is only run in secure mode. With the default `"mode": "insecure"` it is -skipped. The script `scripts/run_benchmarks.sh` respects this by filtering circuits by the `modes` -field in `config.json` before running; see the “Circuit-specific modes” comment and the loop that -builds `RUN_CIRCUITS` there. +Options and secure-only **config** circuit behavior are documented in the script and `config.json`. diff --git a/circuits/bin/config/README.md b/circuits/bin/config/README.md new file mode 100644 index 0000000000..f84ec18629 --- /dev/null +++ b/circuits/bin/config/README.md @@ -0,0 +1,10 @@ +# `config` + +Pre-deployment circuit: checks **secure** DKG + threshold config constants (CRT, bounds, +Reed–Solomon parity matrices, cross-file consistency). + +| | | +| ---------- | -------------------------------------------------------------- | +| **Source** | [`src/main.nr`](src/main.nr) (uses `lib/src/configs/secure/`) | +| **Index** | [Circuit package index](../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/config/src/main.nr b/circuits/bin/config/src/main.nr index ea55977aa9..91a7b9c58e 100644 --- a/circuits/bin/config/src/main.nr +++ b/circuits/bin/config/src/main.nr @@ -4,8 +4,19 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -// This circuit verifies all the configuration parameters used in the secure configs -// Only re-run if parameters change (new deployment) +/// Configuration Verification Circuit (pre-deployment). +/// +/// This circuit verifies all cryptographic parameters used across both BFV (for DKG share encryption) +/// and Threshold BFV (for user data encryption and decryption) schemes. It runs once per deployment to establish +/// the mathematical foundation that all subsequent circuits depend on. +/// +/// Rather than trusting parameter configuration, this circuit provides public proof that: +/// +/// - CRT moduli products are correctly computed +/// - Error bounds satisfy security requirements +/// - Scaling factors have correct multiplicative inverses +/// - Reed-Solomon parity matrices for Shamir sharing are properly constructed +/// - BFV and Threshold BFV parameter sets are mutually consistent use lib::configs::default::{N_PARTIES, T}; use lib::configs::secure::dkg::{ @@ -30,7 +41,7 @@ use lib::configs::secure::threshold::{ PK_GENERATION_R1_BOUNDS, PK_GENERATION_R2_BOUNDS, PK_GENERATION_B_ENC, // share_decryption bounds THRESHOLD_SHARE_DECRYPTION_R1_BOUNDS, THRESHOLD_SHARE_DECRYPTION_R2_BOUNDS, - // user_data_encryption (Greco) bounds + // user_data_encryption bounds USER_DATA_ENCRYPTION_K0IS, USER_DATA_ENCRYPTION_PK_BOUNDS, USER_DATA_ENCRYPTION_E0_BOUND, USER_DATA_ENCRYPTION_E1_BOUND, USER_DATA_ENCRYPTION_U_BOUND, USER_DATA_ENCRYPTION_K1_LOW_BOUND, USER_DATA_ENCRYPTION_K1_UP_BOUND, USER_DATA_ENCRYPTION_R1_LOW_BOUNDS, @@ -40,10 +51,14 @@ use lib::configs::secure::threshold::{ use lib::math::modulo::U128::ModU128; -/// Center a value from [0, modulus) to [-modulus/2, modulus/2). -/// For odd modulus use (raw > t/2). -/// For even use (raw >= t/2). -/// Precondition: raw is in [0, modulus). +/// Centers a value from [0, modulus) to [-modulus/2, modulus/2). +/// +/// This function converts values from standard representation [0, modulus) to +/// centered representation [-modulus/2, modulus/2), which is used in BFV for +/// efficient arithmetic and correct decoding. +/// +/// - For odd modulus: center if raw > modulus/2 +/// - For even modulus: center if raw >= modulus/2 pub fn center(raw: Field, modulus: Field) -> Field { let needs_centering = if (modulus as u128 % 2) == 1 { raw as u128 > modulus as u128 / 2 @@ -58,6 +73,16 @@ pub fn center(raw: Field, modulus: Field) -> Field { } } +/// Main entry point for configuration verification circuit. +/// +/// Verifies all cryptographic parameters across four categories: +/// 1. DKG (BFV) derived values and bounds +/// 2. Threshold BFV derived values and bounds +/// 3. User data encryption bounds +/// 4. Cross-configuration consistency +/// +/// This function will panic if any verification fails, preventing deployment +/// with incorrect parameters. fn main() { // DKG (BFV) Verification verify_dkg_derived_values(); @@ -68,21 +93,27 @@ fn main() { verify_threshold_bounds(); // user_data_encryption Verification - verify_greco_bounds(); + verify_user_data_encryption_bounds(); // Cross-Config Consistency verify_cross_config_consistency(); } -//DKG Derived Values +// === DKG Derived Values === +/// Verifies DKG-specific derived values. +/// +/// Checks: +/// - Q_MOD_T computation (product of CRT moduli mod t) +/// - Q_MOD_T_CENTERED (centered representation) +/// - Reed-Solomon parity matrices for Shamir sharing fn verify_dkg_derived_values() { verify_dkg_q_mod_t(); verify_dkg_q_mod_t_centered(); verify_dkg_parity_matrix(); } -// Verifies DKG: Q_MOD_T = (product of QIS) mod t +/// Verifies DKG: Q_MOD_T = (product of QIS) mod t. fn verify_dkg_q_mod_t() { let t = DKG_PLAINTEXT_MODULUS; let m = ModU128::new(t); @@ -95,14 +126,23 @@ fn verify_dkg_q_mod_t() { assert(product == DKG_Q_MOD_T, "DKG Q_MOD_T verification failed"); } -// Verifies DKG: Q_MOD_T_CENTERED = center(Q_MOD_T) +/// Verifies DKG: Q_MOD_T_CENTERED = center(Q_MOD_T). fn verify_dkg_q_mod_t_centered() { assert( center(DKG_Q_MOD_T, DKG_PLAINTEXT_MODULUS) == DKG_Q_MOD_T_CENTERED, "DKG Q_MOD_T_CENTERED verification failed", ); } -// Verify parity matrix : H * G^T = 0 (mod q_l) for each modulus + +/// Verifies Reed-Solomon parity matrix: H * G^T = 0 (mod q_l) for each modulus. +/// +/// For Shamir Secret Sharing with threshold T, shares must satisfy the parity check +/// equation H * shares^T = 0, where H is the parity check matrix. This function +/// verifies that the precomputed PARITY_MATRIX is correct by checking it against +/// the Vandermonde generator matrix G. +/// +/// The generator matrix G is constructed as G[i][j] = j^i mod q_l (Vandermonde structure). +/// For valid Shamir shares, H * G^T must equal zero for all entries. fn verify_dkg_parity_matrix() { // For each CRT modulus for l in 0..L_THRESHOLD { @@ -139,9 +179,18 @@ fn verify_dkg_parity_matrix() { } } } -//DKG Bounds -// Verifies share_encryption bounds (Circuit 3) +// === DKG Bounds === + +/// Verifies all DKG share encryption bounds (used in circuits C3a/C3b). +/// +/// Checks bounds for: +/// - Encryption randomness u (ternary, bound = 1) +/// - Error terms e0, e1 (bound = 20) +/// - Message bound (t - 1) +/// - Public key bounds per CRT modulus +/// - Quotient bounds r1, r2, p1, p2 +/// - Scaling factors k0[i] satisfy k0[i] * t == -1 (mod q_i) fn verify_dkg_bounds() { let n: u128 = DKG_N as u128; let t: u128 = DKG_PLAINTEXT_MODULUS as u128; @@ -227,15 +276,21 @@ fn verify_dkg_bounds() { } } -//Threshold Derived Values +// === Threshold Derived Values === +/// Verifies Threshold BFV-specific derived values. +/// +/// Checks: +/// - Q_MOD_T computation +/// - Q_MOD_T_CENTERED (centered representation) +/// - Q_INVERSE_MOD_T (multiplicative inverse for decoding) fn verify_threshold_derived_values() { verify_threshold_q_mod_t(); verify_threshold_q_mod_t_centered(); verify_threshold_q_inverse_mod_t(); } -// Verifies Threshold: Q_MOD_T = (product of QIS) mod t +/// Verifies Threshold: Q_MOD_T = (product of QIS) mod t. fn verify_threshold_q_mod_t() { let t = THRESHOLD_PLAINTEXT_MODULUS; let m = ModU128::new(t); @@ -248,7 +303,7 @@ fn verify_threshold_q_mod_t() { assert(product == THRESHOLD_Q_MOD_T, "Threshold Q_MOD_T verification failed"); } -// Verifies Threshold: Q_MOD_T_CENTERED = center(Q_MOD_T) +/// Verifies Threshold: Q_MOD_T_CENTERED = center(Q_MOD_T). fn verify_threshold_q_mod_t_centered() { assert( center(THRESHOLD_Q_MOD_T, THRESHOLD_PLAINTEXT_MODULUS) == THRESHOLD_Q_MOD_T_CENTERED, @@ -256,7 +311,10 @@ fn verify_threshold_q_mod_t_centered() { ); } -// Verifies Threshold: Q * Q_INVERSE_MOD_T = 1 mod t +/// Verifies Threshold: Q * Q_INVERSE_MOD_T == 1 (mod t). +/// +/// This inverse is used in circuit C7 for final decoding: +/// message = -Q^{-1} * (t * u_global)_Q mod t fn verify_threshold_q_inverse_mod_t() { let t = THRESHOLD_PLAINTEXT_MODULUS; let m = ModU128::new(t); @@ -266,14 +324,25 @@ fn verify_threshold_q_inverse_mod_t() { assert(product == 1, "Threshold Q_INVERSE_MOD_T verification failed"); } -//Threshold Bounds +// === Threshold Bounds === +/// Verifies Threshold BFV bounds for key generation and decryption. +/// +/// Checks: +/// - Public key generation bounds +/// - Share decryption bounds fn verify_threshold_bounds() { verify_pk_generation_bounds(); verify_share_decryption_bounds(); } -// Verifies pk_generation bounds (Circuit 1) +/// Verifies pk_generation bounds (Circuit C1). +/// +/// Checks bounds for: +/// - Secret key sk (ternary, bound = 1) +/// - Error eek (bound = 20) +/// - Smudging noise e_sm (large bound for 80-bit statistical security) +/// - Quotients r1, r2 for each CRT modulus fn verify_pk_generation_bounds() { let n: u128 = THRESHOLD_N as u128; let eek: u128 = PK_GENERATION_EEK_BOUND as u128; @@ -303,7 +372,14 @@ fn verify_pk_generation_bounds() { verify_e_sm_bound(); } -// Verifies e_sm_bound (smudging noise bound) +/// Verifies e_sm_bound (smudging noise bound). +/// +/// The smudging noise bound is critical for threshold decryption security. +/// It's computed as: e_sm_bound = 2^lambda * N_CIPHERTEXTS * (b_fresh + Q_MOD_T) +/// where b_fresh = N * e_norm + b_enc + N * b_e * sk_norm and lambda = 80. +/// +/// The 2^80 factor provides 80 bits of statistical security, ensuring that +/// decryption shares don't leak secret key information. fn verify_e_sm_bound() { let n: Field = THRESHOLD_N as Field; let e_norm: Field = 20; @@ -326,7 +402,9 @@ fn verify_e_sm_bound() { assert(expected_e_sm_bound == PK_GENERATION_E_SM_BOUND, "PK_GENERATION_E_SM_BOUND mismatch"); } -// Verifies share_decryption bounds (Circuit 6) +/// Verifies share_decryption bounds (Circuit C6). +/// +/// Checks quotient bounds r1, r2 used in the threshold decryption share equation. fn verify_share_decryption_bounds() { let n: u128 = THRESHOLD_N as u128; @@ -349,11 +427,14 @@ fn verify_share_decryption_bounds() { } } -//user_data_encryption Bounds +// === User Data Encryption Bounds === -// Verifies user_data_encryption (Greco) bounds -// Uses THRESHOLD parameters -fn verify_greco_bounds() { +/// Verifies user_data_encryption bounds. +/// +/// These bounds are used in Phase 3 when users encrypt data to the aggregated +/// threshold public key. Uses Threshold BFV parameters with specific constraints +/// for the user data encryption zero-knowledge proof. +fn verify_user_data_encryption_bounds() { let n: u128 = THRESHOLD_N as u128; let t: u128 = THRESHOLD_PLAINTEXT_MODULUS as u128; let u_bound: u128 = USER_DATA_ENCRYPTION_U_BOUND as u128; @@ -454,8 +535,9 @@ fn verify_greco_bounds() { } } -//Cross-Config Consistency +// === Cross-Config Consistency === +/// Verifies consistency between BFV and Threshold BFV parameter sets. fn verify_cross_config_consistency() { // N is consistent assert(DKG_N == THRESHOLD_N, "N mismatch between DKG and Threshold configs"); diff --git a/circuits/bin/dkg/e_sm_share_computation_base/README.md b/circuits/bin/dkg/e_sm_share_computation_base/README.md new file mode 100644 index 0000000000..ab070b84f1 --- /dev/null +++ b/circuits/bin/dkg/e_sm_share_computation_base/README.md @@ -0,0 +1,9 @@ +# `e_sm_share_computation_base` — C2 (inner) + +Base proof for the **smudging noise** Shamir share array `y`, bound to C1’s `e_sm` commitment. + +| | | +| --------- | --------------------------------------------------------------------------------------------------- | +| **Core** | [`lib/src/core/dkg/share_computation/base.nr`](../../../lib/src/core/dkg/share_computation/base.nr) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/dkg/pk/README.md b/circuits/bin/dkg/pk/README.md index 3edb840650..9f2c6795cc 100644 --- a/circuits/bin/dkg/pk/README.md +++ b/circuits/bin/dkg/pk/README.md @@ -1 +1,9 @@ -instantiation of correct DKG Public Key circuit (PVSS #0) +# `pk` — C0 + +BFV **individual** public key commitment: binds the ciphernode’s share-encryption key used in C3. + +| | | +| --------- | ----------------------------------------------------------------- | +| **Core** | [`lib/src/core/dkg/pk.nr`](../../../lib/src/core/dkg/pk.nr) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/dkg/share_computation/README.md b/circuits/bin/dkg/share_computation/README.md new file mode 100644 index 0000000000..ccf09f5519 --- /dev/null +++ b/circuits/bin/dkg/share_computation/README.md @@ -0,0 +1,12 @@ +# `share_computation` — C2 (final wrapper) + +Verifies **`N_BATCHES`** inner batch proofs (non-ZK UltraHonk verify), folds their commitments, and +checks the VK genealogy (`key_hash`). This is the **ProofType C2a / C2b** surface circuit — upstream +packages are `sk_share_computation_base` / `e_sm_share_computation_base`, `share_computation_chunk`, +`share_computation_chunk_batch`. + +| | | +| --------- | ------------------------------------------------------------------------------------- | +| **Core** | [`lib/src/core/dkg/share_computation/`](../../../lib/src/core/dkg/share_computation/) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/dkg/share_computation/src/main.nr b/circuits/bin/dkg/share_computation/src/main.nr index 8a1cc20ef1..a2fef6379d 100644 --- a/circuits/bin/dkg/share_computation/src/main.nr +++ b/circuits/bin/dkg/share_computation/src/main.nr @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -// Level 2: final_wrapper +// Level 2: final C2 wrapper (after chunk_batch); folds batches and checks VK genealogy. use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; use lib::configs::default::dkg::{ SHARE_COMPUTATION_EXPECTED_VK_HASH_ESM, SHARE_COMPUTATION_EXPECTED_VK_HASH_SK, diff --git a/circuits/bin/dkg/share_computation_chunk/README.md b/circuits/bin/dkg/share_computation_chunk/README.md new file mode 100644 index 0000000000..8f7e30542d --- /dev/null +++ b/circuits/bin/dkg/share_computation_chunk/README.md @@ -0,0 +1,9 @@ +# `share_computation_chunk` — C2 (inner) + +Reed-Solomon parity checks on a **slice** of the public share array `y` (see `PARITY_MATRIX` in configs). + +| | | +| --------- | ----------------------------------------------------------------------------------------------------- | +| **Core** | [`lib/src/core/dkg/share_computation/chunk.nr`](../../../lib/src/core/dkg/share_computation/chunk.nr) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/dkg/share_computation_chunk_batch/README.md b/circuits/bin/dkg/share_computation_chunk_batch/README.md new file mode 100644 index 0000000000..91485d7f4f --- /dev/null +++ b/circuits/bin/dkg/share_computation_chunk_batch/README.md @@ -0,0 +1,10 @@ +# `share_computation_chunk_batch` — C2 (inner) + +Verifies the **base** proof plus a batch of **chunk** proofs and enforces consistency of the `y` +slice across them. + +| | | +| --------- | ----------------------------------------------------------------- | +| **Core** | (invokes base + chunk layouts; configs in `lib`) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/dkg/share_computation_chunk_batch/src/main.nr b/circuits/bin/dkg/share_computation_chunk_batch/src/main.nr index 6cc2a23bd3..1556c6cb9f 100644 --- a/circuits/bin/dkg/share_computation_chunk_batch/src/main.nr +++ b/circuits/bin/dkg/share_computation_chunk_batch/src/main.nr @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -// Level 1: chunk_batch_wrapper +// Level 1: chunk_batch wrapper (verifies base + per-batch chunk proofs). use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; use lib::configs::default::dkg::{ @@ -19,10 +19,10 @@ pub global BASE_PUBLIC_INPUTS: u32 = pub global CHUNK_PUBLIC_INPUTS: u32 = SHARE_COMPUTATION_CHUNK_SIZE * L_THRESHOLD * (N_PARTIES + 1); -// Each batch wrapper takes: -// - base proof (for y consistency) -// - CHUNKS_PER_BATCH chunk proofs -// - CHUNK_BATCH_IDX to know which y slice to check +// Each batch proof bundles: +// - one base proof (`y` share-array layout for consistency), +// - CHUNKS_PER_BATCH chunk proofs, +// - batch_idx (which batch along the chunk sequence). fn main( base_verification_key: UltraHonkVerificationKey, diff --git a/circuits/bin/dkg/share_decryption/README.md b/circuits/bin/dkg/share_decryption/README.md index 6cbed3aa86..df2972edef 100644 --- a/circuits/bin/dkg/share_decryption/README.md +++ b/circuits/bin/dkg/share_decryption/README.md @@ -1 +1,10 @@ -instantiation of correct Smudging Noise Share Decryption circuit (PVSS #4b) +# `share_decryption` — C4a / C4b + +Decrypts incoming shares and aggregates them; outputs commitments consumed by **C6** (threshold +decryption). + +| | | +| --------- | --------------------------------------------------------------------------------------- | +| **Core** | [`lib/src/core/dkg/share_decryption.nr`](../../../lib/src/core/dkg/share_decryption.nr) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/dkg/share_encryption/README.md b/circuits/bin/dkg/share_encryption/README.md index 0040c1cec8..e5d9367483 100644 --- a/circuits/bin/dkg/share_encryption/README.md +++ b/circuits/bin/dkg/share_encryption/README.md @@ -1 +1,10 @@ -instantiation of Smudging Noise Share Encryption circuit (PVSS #3b) +# `share_encryption` — C3a / C3b + +BFV-encrypts each Shamir share under the recipient’s **individual** public key. Same Nargo package +for both variants; witnesses differ (`expected_message_commitment` from C2a vs C2b). + +| | | +| --------- | --------------------------------------------------------------------------------------- | +| **Core** | [`lib/src/core/dkg/share_encryption.nr`](../../../lib/src/core/dkg/share_encryption.nr) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/dkg/sk_share_computation_base/README.md b/circuits/bin/dkg/sk_share_computation_base/README.md new file mode 100644 index 0000000000..0b0150ce32 --- /dev/null +++ b/circuits/bin/dkg/sk_share_computation_base/README.md @@ -0,0 +1,9 @@ +# `sk_share_computation_base` — C2 (inner) + +Base proof for the **secret key contribution** Shamir share array `y`, bound to C1’s `sk` commitment. + +| | | +| --------- | --------------------------------------------------------------------------------------------------- | +| **Core** | [`lib/src/core/dkg/share_computation/base.nr`](../../../lib/src/core/dkg/share_computation/base.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/fold/README.md b/circuits/bin/recursive_aggregation/fold/README.md new file mode 100644 index 0000000000..5ce4c28170 --- /dev/null +++ b/circuits/bin/recursive_aggregation/fold/README.md @@ -0,0 +1,13 @@ +# `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 index 957e439f28..3f79183aa7 100644 --- a/circuits/bin/recursive_aggregation/fold/src/main.nr +++ b/circuits/bin/recursive_aggregation/fold/src/main.nr @@ -30,7 +30,7 @@ fn main( proof2_key_hash, ); - // Hash the two commitments with Poseidon so the verifier can check the folded proof used the expected public inputs. + // 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]); diff --git a/circuits/bin/recursive_aggregation/wrapper/README.md b/circuits/bin/recursive_aggregation/wrapper/README.md new file mode 100644 index 0000000000..1ad9ea0daa --- /dev/null +++ b/circuits/bin/recursive_aggregation/wrapper/README.md @@ -0,0 +1,45 @@ +# 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` (batch key hash + inner proof’s public outputs; final C2 proof wrapped **one at a time**) | +| `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/share_computation/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/src/main.nr index 727d156e17..dd456d87ec 100644 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/src/main.nr +++ b/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/src/main.nr @@ -7,9 +7,9 @@ use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; use lib::math::commitments::compute_recursive_aggregation_commitment; -// Each SK/ESM final C2 proof is wrapped individually after the two-level pipeline. +// Each SK/ESM final C2 proof is wrapped individually after the base -> chunk -> batch pipeline. pub global N_PROOFS: u32 = 1; -// The final share_computation circuit exposes 3 public outputs: +// The final share_computation wrapper exposes 3 public outputs: // batch_key_hash (pub param) + (key_hash, commitment) return tuple. pub global N_PUBLIC_INPUTS: u32 = 3; diff --git a/circuits/bin/threshold/decrypted_shares_aggregation/README.md b/circuits/bin/threshold/decrypted_shares_aggregation/README.md new file mode 100644 index 0000000000..efdc80cc87 --- /dev/null +++ b/circuits/bin/threshold/decrypted_shares_aggregation/README.md @@ -0,0 +1,10 @@ +# `decrypted_shares_aggregation` — C7 + +Combines **T+1** decryption shares (commitments from C6), Lagrange weights, and CRT data to recover +the plaintext polynomial / message. + +| | | +| --------- | --------------------------------------------------------------------------------------------------------------------------- | +| **Core** | [`lib/src/core/threshold/decrypted_shares_aggregation.nr`](../../../lib/src/core/threshold/decrypted_shares_aggregation.nr) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/threshold/pk_aggregation/README.md b/circuits/bin/threshold/pk_aggregation/README.md index 7644350ebc..d7567218ed 100644 --- a/circuits/bin/threshold/pk_aggregation/README.md +++ b/circuits/bin/threshold/pk_aggregation/README.md @@ -1 +1,10 @@ -instantiation of Threshold Public Key Aggregation circuit (PVSS #5) +# `pk_aggregation` — C5 + +Aggregates honest parties’ threshold **public key** contributions and checks consistency with C1 +commitments. + +| | | +| --------- | ----------------------------------------------------------------------------------------------- | +| **Core** | [`lib/src/core/threshold/pk_aggregation.nr`](../../../lib/src/core/threshold/pk_aggregation.nr) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/threshold/pk_generation/README.md b/circuits/bin/threshold/pk_generation/README.md index 57a498a870..1a3709a24a 100644 --- a/circuits/bin/threshold/pk_generation/README.md +++ b/circuits/bin/threshold/pk_generation/README.md @@ -1 +1,10 @@ -instantiation of correct Threshold Public Key Generation circuit (PVSS #1) +# `pk_generation` — C1 + +TrBFV **threshold public key** contribution: proves correct generation of `pk` share, `sk`, and +smudging noise commitments (Schwartz–Zippel style checks). + +| | | +| --------- | --------------------------------------------------------------------------------------------- | +| **Core** | [`lib/src/core/threshold/pk_generation.nr`](../../../lib/src/core/threshold/pk_generation.nr) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/threshold/share_decryption/README.md b/circuits/bin/threshold/share_decryption/README.md index 07caf5bb80..5ca5265889 100644 --- a/circuits/bin/threshold/share_decryption/README.md +++ b/circuits/bin/threshold/share_decryption/README.md @@ -1 +1,10 @@ -instantiation of Threshold Share Decryption circuit (PVSS #6) +# `share_decryption` — C6 + +Threshold **decryption share** for the homomorphic result ciphertext, using aggregated `sk` / `e_sm` +commitments from C4. + +| | | +| --------- | --------------------------------------------------------------------------------------------------- | +| **Core** | [`lib/src/core/threshold/share_decryption.nr`](../../../lib/src/core/threshold/share_decryption.nr) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/threshold/user_data_encryption/README.md b/circuits/bin/threshold/user_data_encryption/README.md new file mode 100644 index 0000000000..ba7eecc146 --- /dev/null +++ b/circuits/bin/threshold/user_data_encryption/README.md @@ -0,0 +1,9 @@ +# `user_data_encryption` — P3 (wrapper) + +Re-verifies **ct0** and **ct1** proofs, asserts identical **`u_commitment`**, and outputs aggregated +pk / ciphertext / k1 commitments for downstream use. + +| | | +| --------- | ----------------------------------------------------------------- | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/threshold/user_data_encryption/src/main.nr b/circuits/bin/threshold/user_data_encryption/src/main.nr index b13d5ff72a..0e570310e5 100644 --- a/circuits/bin/threshold/user_data_encryption/src/main.nr +++ b/circuits/bin/threshold/user_data_encryption/src/main.nr @@ -8,12 +8,12 @@ use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_hon use lib::math::commitments::{compute_commitment, DS_CIPHERTEXT, DS_PK_AGGREGATION}; fn main( - // User Data Encryption ct0 Section. + // P3: ct0 inner proof. ct0_verification_key: UltraHonkVerificationKey, ct0_proof: UltraHonkProof, ct0_public_inputs: [Field; 4], // pk0_commitment, ct0_commitment, k1_commitment, u_commitment ct0_key_hash: pub Field, - // User Data Encryption ct1 Section. + // P3: ct1 inner proof. ct1_verification_key: UltraHonkVerificationKey, ct1_proof: UltraHonkProof, ct1_public_inputs: [Field; 3], // pk1_commitment, ct1_commitment, u_commitment @@ -41,7 +41,7 @@ fn main( ct_inputs.push(ct1_public_inputs[1]); let ct_commitment = compute_commitment(ct_inputs, DS_CIPHERTEXT); - // Computes the public key aggregation commitment. + // Threshold PK aggregation commitment (pk0 and pk1 limbs). let mut pk_inputs = Vec::new(); pk_inputs.push(ct0_public_inputs[0]); pk_inputs.push(ct1_public_inputs[0]); diff --git a/circuits/bin/threshold/user_data_encryption_ct0/README.md b/circuits/bin/threshold/user_data_encryption_ct0/README.md new file mode 100644 index 0000000000..a57f9f0991 --- /dev/null +++ b/circuits/bin/threshold/user_data_encryption_ct0/README.md @@ -0,0 +1,10 @@ +# `user_data_encryption_ct0` — P3 (inner) + +First leg of user BFV encryption under the **aggregated** threshold public key (paired with +`user_data_encryption_ct1`). + +| | | +| --------- | ------------------------------------------------------------------------------------------------------------------- | +| **Core** | [`lib/src/core/threshold/user_data_encryption_ct0.nr`](../../../lib/src/core/threshold/user_data_encryption_ct0.nr) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/threshold/user_data_encryption_ct1/README.md b/circuits/bin/threshold/user_data_encryption_ct1/README.md new file mode 100644 index 0000000000..ef423eef5b --- /dev/null +++ b/circuits/bin/threshold/user_data_encryption_ct1/README.md @@ -0,0 +1,9 @@ +# `user_data_encryption_ct1` — P3 (inner) + +Second leg of user BFV encryption (paired with `user_data_encryption_ct0`). + +| | | +| --------- | ------------------------------------------------------------------------------------------------------------------- | +| **Core** | [`lib/src/core/threshold/user_data_encryption_ct1.nr`](../../../lib/src/core/threshold/user_data_encryption_ct1.nr) | +| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | +| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/lib/src/README.md b/circuits/lib/src/README.md new file mode 100644 index 0000000000..a945270634 --- /dev/null +++ b/circuits/lib/src/README.md @@ -0,0 +1,64 @@ +# Noir library (`circuits/lib/src/`) + +Every Nargo package under `circuits/bin/` depends on this library containing the shared **PVSS / +BFV** constraint logic: polynomials, commitments, SAFE hashing, modular arithmetic, and the +`core/dkg` and `core/threshold` circuit bodies that binaries wrap. + +For **which** binary package maps to **C0–C7** and **`CircuitName`**, see the +[**circuit package index**](../../README.md#circuit-package-index) in +[`circuits/README.md`](../../README.md); for protocol phases and the PV-TBFV picture, read +[Cryptography](https://docs.theinterfold.com/cryptography) +([`docs/pages/cryptography.mdx`](../../../docs/pages/cryptography.mdx)). + +```text +lib/src/ +├── math/ # polynomials, SAFE sponge, helpers, ModU128, commitments +├── core/ # dkg/ and threshold/ circuit structs (`execute()` entry points) +└── configs/ # BFV / CRT presets; wired via `configs::default` (see `default/mod.nr`) +``` + +```mermaid +flowchart LR + math["math"] --> core["core"] + configs["configs"] --> core + core --> dkg["dkg"] + core --> threshold["threshold"] +``` + +## math + +| Area | Contents | +| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **polynomial** | Coefficients in descending order; `eval` / `eval_mod`, range checks | +| **safe** | [SAFE](https://hackmd.io/@7dpNYqjKQGeYC7wMlPxHtQ/ByIbpfX9c) sponge in `safe.nr`: **Poseidon2** permutation + **Keccak256** as required by the construction; commitments & challenges | +| **helpers** | `flatten`, `pack`, `compute_safe` for witness hashing | +| **modulo** | `ModU128` constrained modular arithmetic | +| **commitments** | Domain-separated `DS_*`; `compute_dkg_pk_commitment`, `compute_threshold_pk_commitment`, share encryption/computation helpers, etc. | + +## core + +| Module | Circuits | Role | +| -------------------------------------------- | -------- | ------------------------------------------ | +| `dkg/pk` | C0 | Individual pk commitment | +| `dkg/share_computation/` | C2 | Base + chunk + parity; `execute()` layouts | +| `dkg/share_encryption` | C3 | Encrypt share under recipient pk | +| `dkg/share_decryption` | C4 | Decrypt and aggregate | +| `threshold/pk_generation` | C1 | TrBFV contribution | +| `threshold/pk_aggregation` | C5 | Aggregate pk shares | +| `threshold/user_data_encryption_ct0` / `ct1` | P3 | User encryption legs | +| `threshold/share_decryption` | C6 | Threshold decryption share | +| `threshold/decrypted_shares_aggregation` | C7 | Final plaintext | + +## configs + +Switch presets in `configs/default/mod.nr` (`pub use super::secure::dkg` / `threshold`). Each preset +defines `N`, `L`, `QIS`, bounds, `PARITY_MATRIX`, per-circuit `Configs`, and +`MAX_MSG_NON_ZERO_COEFFS` (C7 plaintext sparsity). + +## Related documentation + +| Topic | Location | +| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| Binary packages, `CircuitName`, build and test | [`circuits/README.md`](../../README.md) | +| Phases, PV-TBFV, circuit identifiers | [Cryptography](https://docs.theinterfold.com/cryptography) · [source](../../../docs/pages/cryptography.mdx) | +| Toolchain, `enclave noir`, compile scripts | [Noir Circuits](https://docs.theinterfold.com/noir-circuits) · [source](../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/lib/src/configs/insecure/threshold.nr b/circuits/lib/src/configs/insecure/threshold.nr index 67a646f9ad..36e5c6a4df 100644 --- a/circuits/lib/src/configs/insecure/threshold.nr +++ b/circuits/lib/src/configs/insecure/threshold.nr @@ -1181,7 +1181,6 @@ pub global THRESHOLD_SHARE_DECRYPTION_CONFIGS: ShareDecryptionConfigs = Share decrypted_shares_aggregation (CIRCUIT 7) ------------------------------------- ************************************/ - pub global DECRYPTED_SHARES_AGGREGATION_BIT_NOISE: u32 = 65; pub global DECRYPTED_SHARES_AGGREGATION_BIT_D: u32 = 35; diff --git a/circuits/lib/src/configs/secure/threshold.nr b/circuits/lib/src/configs/secure/threshold.nr index 9e16c89d0d..91c45600ee 100644 --- a/circuits/lib/src/configs/secure/threshold.nr +++ b/circuits/lib/src/configs/secure/threshold.nr @@ -32931,7 +32931,6 @@ pub global THRESHOLD_SHARE_DECRYPTION_CONFIGS: ShareDecryptionConfigs = Share decrypted_shares_aggregation (CIRCUIT 7) ------------------------------------- ************************************/ - pub global DECRYPTED_SHARES_AGGREGATION_BIT_NOISE: u32 = 200; pub global DECRYPTED_SHARES_AGGREGATION_BIT_D: u32 = 52; diff --git a/circuits/lib/src/core/dkg/pk.nr b/circuits/lib/src/core/dkg/pk.nr index 2745b29efd..81fdd762e2 100644 --- a/circuits/lib/src/core/dkg/pk.nr +++ b/circuits/lib/src/core/dkg/pk.nr @@ -7,22 +7,33 @@ use crate::math::commitments::compute_dkg_pk_commitment; use crate::math::polynomial::Polynomial; -/// Correct DKG Public Key Circuit (Circuit 0). +/// DKG public key commitment (C0). +/// +/// **Role:** Bind the ciphernode DKG public key used to encrypt outgoing shares in DKG. +/// +/// **Produces:** +/// - `commit(pk_dkg)` -> C3 (share encryption). pub struct Pk { - /// Correct DKG public key components - /// pk0[i] is the first component for modulus i + /// DKG public key first component for each CRT modulus. + /// pk0[i] is a degree N-1 polynomial for modulus q_i. pk0: [Polynomial; L], - /// pk1[i] is the second component for modulus i + /// DKG public key second component for each CRT modulus. + /// pk1[i] is a degree N-1 polynomial for modulus q_i. pk1: [Polynomial; L], } impl Pk { + /// Creates a new DKG public key commitment circuit instance. + /// + /// # Arguments + /// + /// * `pk0` - First component of DKG public key (one polynomial per CRT modulus) + /// * `pk1` - Second component of DKG public key (one polynomial per CRT modulus) pub fn new(pk0: [Polynomial; L], pk1: [Polynomial; L]) -> Self { Pk { pk0, pk1 } } - /// Main verification function - /// Returns commitment to correct DKG public key + /// Executes the commitment circuit and returns the binding commitment. pub fn execute(self) -> Field { compute_dkg_pk_commitment::(self.pk0, self.pk1) } diff --git a/circuits/lib/src/core/dkg/share_computation/base.nr b/circuits/lib/src/core/dkg/share_computation/base.nr index 44420f2082..b70f30d94e 100644 --- a/circuits/lib/src/core/dkg/share_computation/base.nr +++ b/circuits/lib/src/core/dkg/share_computation/base.nr @@ -4,10 +4,8 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -// Base circuit for C2 (SecretKeyShareComputation and SmudgingNoiseShareComputation). -// Verifies secret commitment and consistency, and outputs party commitments for -// downstream use in C3/C4. y is public to allow the wrapper to enforce consistency -// with chunk circuits without hashing overhead. +//! C2 base circuits - SK and e_sm share-array verification (`SecretKeyShareComputationBase`, +//! `SmudgingNoiseShareComputationBase`). use crate::math::commitments::{ compute_share_computation_e_sm_commitment, compute_share_computation_sk_commitment, @@ -15,15 +13,21 @@ use crate::math::commitments::{ }; use crate::math::polynomial::Polynomial; -// ===== BASE CIRCUIT FOR SK ===== +// --- C2a: SK secret shares --- +/// C2a base - bind `sk_secret` and public share array `y` to C1; emit per-party commitments for C3. +/// +/// **Role:** `y[coeff][mod][0]` matches `sk_secret`; party columns hash to C3-facing commitments. +/// +/// **Consumes:** `expected_secret_commitment` from C1 (sk branch). +/// +/// **Produces:** `[[Field; L]; N_PARTIES]` party commitments for C3. pub struct SecretKeyShareComputationBase { /// Expected commitment to sk secret (from C1, public input) expected_secret_commitment: Field, /// Secret key polynomial sk_secret: Polynomial, - /// Full shares array y[coeff_idx][mod_idx][party_idx] - /// Public so wrapper can enforce consistency with chunk circuits + /// Public share array `y[coeff][mod][party]`; wrappers/chunks enforce equality. y: [[[Field; N_PARTIES + 1]; L]; N], } @@ -72,23 +76,32 @@ impl [[Field; L]; N_PARTIES] { + // Step 1: Bind `sk_secret` to C1. self.verify_secret_commitment(); + // Step 2: Align `y[..][..][0]` with `sk_secret`. self.verify_secret_consistency(); + // Step 3: Hash party columns for C3. self.compute_party_commitments() } } -// ===== BASE CIRCUIT FOR ESM ===== +// --- C2b: e_sm shares --- +/// C2b base - bind `e_sm_secret` and public share array `y` to C1; emit per-party commitments for C3. +/// +/// **Role:** Same layout as C2a, for the smudging-noise branch. +/// +/// **Consumes:** `expected_secret_commitment` from C1 (e_sm branch). +/// +/// **Produces:** `[[Field; L]; N_PARTIES]` party commitments for C3. pub struct SmudgingNoiseShareComputationBase { /// Expected commitment to e_sm secret (from C1, public input) expected_secret_commitment: Field, /// Smudging noise polynomial per modulus e_sm_secret: [Polynomial; L], - /// Full shares array y[coeff_idx][mod_idx][party_idx] - /// Public so wrapper can enforce consistency with chunk circuits + /// Public share array `y[coeff][mod][party]`; wrappers/chunks enforce equality. y: [[[Field; N_PARTIES + 1]; L]; N], } @@ -137,10 +150,13 @@ impl [[Field; L]; N_PARTIES] { + // Step 1: Bind `e_sm_secret` to C1. self.verify_secret_commitment(); + // Step 2: Align `y[..][..][0]` with `e_sm_secret` per modulus. self.verify_secret_consistency(); + // Step 3: Hash party columns for C3. self.compute_party_commitments() } } diff --git a/circuits/lib/src/core/dkg/share_computation/chunk.nr b/circuits/lib/src/core/dkg/share_computation/chunk.nr index 5dc4722ea7..26a2c65a4d 100644 --- a/circuits/lib/src/core/dkg/share_computation/chunk.nr +++ b/circuits/lib/src/core/dkg/share_computation/chunk.nr @@ -4,13 +4,12 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -// Chunk circuit for C2. Verifies range checks and parity check for a chunk of -// CHUNK_SIZE coefficients. y_chunk is public so the wrapper can enforce consistency -// with the base circuit without hashing overhead in either circuit. +//! C2 chunk circuit - range + parity on a slice of the public share array `y`. use crate::math::modulo::U128::ModU128; use crate::math::polynomial::Polynomial; +/// Parameters for share computation chunk (C2). pub struct Configs { pub qis: [Field; L], } @@ -21,6 +20,13 @@ impl Configs { } } +/// Share computation chunk (C2). +/// +/// **Role:** For a slice of coefficients, range-check Shamir shares and Reed-Solomon parity (`H`). +/// +/// **Consumes:** Public `y_chunk` aligned with the C2 base `y` array. +/// +/// **Produces:** None (assert-only); wrappers glue chunks to the base proof. pub struct ShareComputationChunk { configs: Configs, /// Slice of y for this chunk, public so wrapper can enforce @@ -72,7 +78,9 @@ impl C6 (threshold share decryption). +/// - C4b: `commit(agg_e_sm)` -> C6 (threshold share decryption). pub struct ShareDecryption { - /// Expected commitments from Circuit 3 for H honest parties: [party_idx][mod_idx] - /// (public witness) + /// Expected commitments to the share polynomials, produced in C2a (for C4a) or C2b (for C4b) + /// via commit_to_party_shares. Organised as [party_idx][mod_idx], covering all H honest + /// parties and L CRT moduli. + /// (public witnesses) expected_commitments: [[Field; L]; H], - /// Decrypted shares from H honest parties: [party_idx][mod_idx] + /// Decrypted share polynomials from H honest parties. + /// Organised as [party_idx][mod_idx]. /// (secret witnesses) decrypted_shares: [[Polynomial; L]; H], } @@ -33,7 +40,12 @@ impl ShareDecryption ShareDecryption [Polynomial; L] { let mut sum: [Polynomial; L] = [Polynomial::new([0; N]); L]; @@ -68,16 +91,19 @@ impl ShareDecryption Field { - // Step 1: Verify all commitments match + // Step 1: Verify decrypted shares match expected C2 commitments. self.verify_commitments(); - // Step 2: Compute aggregated shares + // Step 2: Sum shares coefficient-wise per CRT limb. let aggregated = self.compute_aggregated_shares(); - // Step 3: Return commitment to aggregated shares + // Step 3: Publish commitment to the aggregate (C6 input). compute_aggregated_shares_commitment::(aggregated) } } diff --git a/circuits/lib/src/core/dkg/share_encryption.nr b/circuits/lib/src/core/dkg/share_encryption.nr index 1f48c41d53..6410f17c08 100644 --- a/circuits/lib/src/core/dkg/share_encryption.nr +++ b/circuits/lib/src/core/dkg/share_encryption.nr @@ -12,35 +12,35 @@ use crate::math::helpers::flatten; use crate::math::modulo::U128::ModU128; use crate::math::polynomial::Polynomial; -/// Cryptographic parameters for DKG share encryption circuit. +/// Parameters for DKG share encryption (C3). pub struct Configs { /// Plaintext modulus t pub t: Field, - /// Q mod t (for scaling message) + /// Q mod t: product of all CRT moduli modulo the plaintext modulus, used for message scaling pub q_mod_t: Field, - /// CRT moduli for each basis: [q_0, q_1, ..., q_{L-1}] + /// CRT moduli: [q_0, q_1, ..., q_{L-1}] pub qis: [Field; L], - /// Scaling factors for each basis: [k0_0, k0_1, ..., k0_{L-1}] + /// Scaling factors k0[i] satisfying `k0[i] * t == -1 (mod q_i)` for each CRT modulus pub k0is: [Field; L], - /// Bounds for public key polynomials for each CRT basis + /// Coefficient bounds for public key polynomials per CRT modulus: (q_i - 1) / 2 pub pk_bounds: [Field; L], - /// Bounds for error polynomials (e0) + /// Coefficient bound for the global error polynomial e0 pub e0_bound: Field, - /// Bounds for error polynomials (e1) + /// Coefficient bound for the error polynomial e1 pub e1_bound: Field, - /// Bound for secret polynomial u (ternary distribution) + /// Coefficient bound for the ternary randomness polynomial u (= 1) pub u_bound: Field, - /// Lower bounds for r1 polynomials (modulus switching quotients) + /// Lower bounds for r1 modulus switching quotient coefficients per CRT modulus pub r1_low_bounds: [Field; L], - /// Upper bounds for r1 polynomials (modulus switching quotients) + /// Upper bounds for r1 modulus switching quotient coefficients per CRT modulus pub r1_up_bounds: [Field; L], - /// Bounds for r2 polynomials (cyclotomic reduction quotients) + /// Coefficient bounds for r2 cyclotomic reduction quotients per CRT modulus pub r2_bounds: [Field; L], - /// Bounds for p1 polynomials (modulus switching quotients) + /// Coefficient bounds for p1 modulus switching quotients per CRT modulus pub p1_bounds: [Field; L], - /// Bounds for p2 polynomials (cyclotomic reduction quotients) + /// Coefficient bounds for p2 cyclotomic reduction quotients per CRT modulus pub p2_bounds: [Field; L], - /// Bound for message polynomial (m) + /// Bound for the raw message (share) polynomial coefficients: t - 1 pub msg_bound: Field, } @@ -80,20 +80,23 @@ impl Configs { } } -/// DKG Share Encryption Circuit (Circuit 3). +/// DKG share encryption (C3a / C3b). /// -/// Verifies: -/// 1. Public key commitment matches expected (from Circuit 0) -/// 2. Message commitment matches expected (from SK shares circuit) -/// 3. Correct DKG share encryption: ct0[l] = pk0[l] * u + e0[l] + k1 * k0[l] + r1[l] * q[l] + r2[l] * (X^N + 1) -/// and ct1[l] = pk1[l] * u + e1 + p2[l] * (X^N + 1) + p1[l] * q[l] +/// **Role:** Prove BFV encryption of a share under the recipient DKG public key. Run twice per +/// recipient (sk share and e_sm share). +/// +/// **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). pub struct ShareEncryption { /// Circuit parameters configs: Configs, - /// Expected commitment to public key (from Circuit 0) + /// Expected commitment to the DKG public key (from C0: pk). /// (public witness) expected_pk_commitment: Field, - /// Expected commitment to message (from SK shares verification circuit) + /// Expected commitment to the share (message) being encrypted. + /// - C3a: commit(sk_share[party_idx][mod_idx]) produced by C2a + /// - C3b: commit(e_sm_share[party_idx][mod_idx]) produced by C2b /// (public witness) expected_message_commitment: Field, /// Public key component 0 for each CRT basis (committed witnesses) @@ -167,7 +170,10 @@ impl(self.pk0is, self.pk1is) @@ -176,7 +182,12 @@ impl(self.message) @@ -185,8 +196,11 @@ impl Polynomial { let t = self.configs.t; let t_mod = ModU128::new(t); @@ -217,7 +231,11 @@ impl) -> Vec { let mut inputs = Vec::new(); @@ -247,7 +265,13 @@ impl(self.configs.u_bound, self.configs.u_bound); self.e0.range_check_2bounds::(self.configs.e0_bound, self.configs.e0_bound); @@ -323,14 +347,27 @@ impl) -> Vec { let inputs = self.payload(k1); compute_share_encryption_challenge::(inputs) } - /// Verifies DKG encryption constraints using Fiat-Shamir challenges and the Schwartz-Zippel lemma + /// Verifies BFV encryption equations using Schwartz-Zippel with batched modulus verification. + /// + /// For each CRT modulus l, checks at the primary challenge point gamma: + /// ct0[l](gamma) = pk0[l](gamma)*u(gamma) + e0[l](gamma) + k1(gamma)*k0[l] + r1[l](gamma)*q_l + r2[l](gamma)*(gamma^N + 1) + /// ct1[l](gamma) = pk1[l](gamma)*u(gamma) + e1(gamma) + p2[l](gamma)*(gamma^N + 1) + p1[l](gamma)*q_l + /// + /// Rather than asserting each equation independently, both sides are accumulated + /// into a weighted batch sum using additional challenge values as weights. + /// A deviation in any single equation will cause the accumulated sums to diverge + /// with overwhelming probability (Schwartz-Zippel soundness). fn verify_evaluations(self, gammas: Vec, k1: Polynomial) { let gamma = gammas.get(0); let cyclo_at_gamma = gamma.pow_32(N as Field) + 1; @@ -345,14 +382,14 @@ impl { /// CRT moduli: [q_0, q_1, ..., q_{L-1}] pub qis: [Field; L], @@ -27,14 +27,14 @@ impl Configs { } } -/// Decrypted Shares Aggregation Circuit (Circuit 7). -/// Uses BigNum for Q values (works for both insecure and secure parameter sets). +/// Decrypted shares aggregation (C7). /// -/// Verifies: -/// 0. Each party's decryption share matches the corresponding C6 public `d` commitment -/// 1. Lagrange interpolation to compute u^{(l)} for each CRT basis -/// 2. CRT reconstruction: u^{(l)} + r^{(l)} * q_l = u_global -/// 3. Decoding verification: message = -Q^{-1} * (t * u_global)_Q mod t +/// **Role:** Combine partial decryption shares into the plaintext message (CRT + decode). Uses +/// BigNum where the product of CRT moduli exceeds the field. +/// +/// **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 { /// Circuit parameters including crypto constants configs: Configs, @@ -80,7 +80,7 @@ impl( diff --git a/circuits/lib/src/core/threshold/pk_aggregation.nr b/circuits/lib/src/core/threshold/pk_aggregation.nr index 91efaf8326..7c93ef11e1 100644 --- a/circuits/lib/src/core/threshold/pk_aggregation.nr +++ b/circuits/lib/src/core/threshold/pk_aggregation.nr @@ -8,7 +8,7 @@ use crate::math::commitments::{compute_pk_aggregation_commitment, compute_thresh use crate::math::modulo::U128::ModU128; use crate::math::polynomial::Polynomial; -/// Cryptographic parameters for Threshold public key aggregation circuit. +/// Parameters for threshold public key aggregation (C5). pub struct Configs { /// CRT moduli for each basis: [q_0, q_1, ..., q_{L-1}] pub qis: [Field; L], @@ -20,35 +20,42 @@ impl Configs { } } -/// Public Key Aggregation (Circuit 5). +/// Threshold public key aggregation (C5). /// -/// Verifies that for each CRT basis l and each coefficient i: -/// - pk0_agg[l][i] = sum_h(pk0[h][l][i]) mod q_l (witnesses use centered coefficients; aggregation -/// uses a half-q shift so [`ModU128::reduce_mod`] sees small non-negative integers) -/// - pk1_agg[l][i] = pk1[0][l][i] (all parties share pk1 = a, aggregated pk1 = a) +/// **Role:** Sum honest parties' threshold PK limbs into `pk0_agg`, check `pk1_agg` matches CRS `a`, +/// and commit the aggregated key for P3. +/// +/// **Consumes:** `commit(pk_trbfv[h])` from C1 per honest party `h`. +/// +/// **Produces:** `commit(pk_agg)` for user-data encryption (P3). +/// +/// **Verifies:** For each limb and coefficient, `pk0_agg` matches the mod-`q_l` sum of party `pk0` +/// (centered coefficients, `ModU128::reduce_mod` with half-`q` shift); `pk1_agg` equals shared CRS `a`. pub struct PkAggregation { /// Circuit parameters including CRT moduli configs: Configs, - /// Expected commitments to threshold public key (from C1) - /// We need one commitment from each honest party (H). - /// (public witness) + /// Expected commitments to each honest party's threshold public key share. + /// commit(pk_trbfv[h]) produced by C1 for each h in H. + /// (public witnesses) expected_threshold_pk_commitments: [Field; H], - /// Individual public keys from H honest parties - /// pk0[party_idx][basis_idx] - first component of public key for each party and CRT basis + /// Individual threshold public key first components from H honest parties. + /// pk0[party_idx][basis_idx] for each party and CRT basis. /// (committed witnesses) pk0: [[Polynomial; L]; H], - /// pk1[party_idx][basis_idx] - second component of public key for each party and CRT basis + /// Individual threshold public key second components from H honest parties. + /// pk1[party_idx][basis_idx] for each party and CRT basis. /// (committed witnesses) pk1: [[Polynomial; L]; H], - /// Claimed aggregated public key - /// pk0_agg[basis_idx] - first component of aggregated public key for each CRT basis - /// (committed witnesses) + /// Claimed aggregated public key first component for each CRT basis. + /// Must equal sum(pk0[h][l]) mod q_l for each basis l. + /// (committed witness) pk0_agg: [Polynomial; L], - /// pk1_agg[basis_idx] - second component of aggregated public key for each CRT basis - /// (committed witnesses) + /// Claimed aggregated public key second component for each CRT basis. + /// Must equal sum(pk1[h][l]) mod q_l for each basis l. + /// (committed witness) pk1_agg: [Polynomial; L], } @@ -64,7 +71,11 @@ impl PkAggregation PkAggregation; L]; H], pk_agg: [Polynomial; L], @@ -114,19 +132,18 @@ impl PkAggregation Field { - // 0. Verify pk commitments + // Step 1: Bind each party PK to its C1 commitment. self.verify_pk_commitments(); - // 1. Verify pk0 aggregation (sum) and pk1 (all equal to a, pk1_agg = a) + // Step 2: Check `pk0_agg` sums party limbs and `pk1_agg` matches CRS `a` (per CRT basis). for basis_idx in 0..L { - self.verify_pk0_for_basis(self.pk0, self.pk0_agg, basis_idx); + self.verify_pk_for_basis(self.pk0, self.pk0_agg, basis_idx); self.verify_pk1(basis_idx); } - // 2. Commit to aggregated threshold public key + // Step 3: Commit to the aggregated threshold public key. compute_pk_aggregation_commitment::(self.pk0_agg, self.pk1_agg) } } diff --git a/circuits/lib/src/core/threshold/pk_generation.nr b/circuits/lib/src/core/threshold/pk_generation.nr index a8d38711d8..aa2b0de233 100644 --- a/circuits/lib/src/core/threshold/pk_generation.nr +++ b/circuits/lib/src/core/threshold/pk_generation.nr @@ -11,7 +11,7 @@ use crate::math::commitments::{ use crate::math::helpers::flatten; use crate::math::polynomial::Polynomial; -/// Cryptographic parameters for threshold public key generation circuit. +/// Parameters for threshold public key generation (C1). pub struct Configs { /// CRT moduli: [q_0, q_1, ..., q_{L-1}] pub qis: [Field; L], @@ -40,17 +40,17 @@ impl Configs { } } -/// Correct Threshold Public Key Generation Circuit (Circuit 1). +/// Threshold public key contribution (C1). /// -/// Verifies: -/// 1. Range checks on all secret witnesses (secret key, error, smudging noise, quotients) -/// 2. Correct public key generation: pk0_i = -a_i * sk + eek + r2_i * (X^N + 1) + r1_i * q_i -/// (pk1 is a_i from the hardcoded CRS `a`, not a separate witness) +/// **Role:** Prove one party's TrBFV public-key contribution from hidden `sk`, `eek`, `e_sm`, and +/// quotients (`pk1` limb is the CRS `a`). /// -/// Outputs: -/// - commit(threshold_sk) -/// - commit(threshold_pk) -/// - commit(e_sm) +/// **Verifies:** Range checks on witnesses; evaluation form of `pk0 = -a*sk + eek + ...` at the FS challenge. +/// +/// **Produces (tuple order):** +/// - `commit(sk)` -> C2a (share computation). +/// - `commit(pk_trbfv)` -> C5 (pk aggregation). +/// - `commit(e_sm)` -> C2b (share computation). pub struct PkGeneration { /// Cryptographic parameters including bounds, moduli, and constants. configs: Configs, @@ -95,7 +95,10 @@ impl Vec { let mut inputs = Vec::new(); @@ -116,30 +119,41 @@ impl (Field, Field, Field) { + // Step 1: Range-check secret witnesses (sk, eek, e_sm, quotients). self.perform_range_checks(); + // Step 2: Derive commitments for the FS transcript. let sk_commitment = compute_share_computation_sk_commitment::(self.sk); let e_sm_commitment = compute_share_computation_e_sm_commitment::(self.e_sm); let pk_commitment = compute_threshold_pk_commitment::(self.pk0, self.a); + // Step 3: Fiat-Shamir challenge and per-modulus evaluation checks. let gamma = self.generate_challenge(sk_commitment, pk_commitment); self.verify_evaluations(gamma); + // Step 4: Return commitments for downstream circuits. (sk_commitment, pk_commitment, e_sm_commitment) } - /// Generates Fiat-Shamir challenge values using the SAFE cryptographic sponge + /// Generates Fiat-Shamir challenge values using the SAFE cryptographic sponge. + /// + /// Returns a single challenge point `gamma` used to verify key generation + /// equations for all CRT moduli via the Schwartz-Zippel lemma. fn generate_challenge(self, sk_commitment: Field, pk_commitment: Field) -> Field { let inputs = self.payload(sk_commitment, pk_commitment); compute_threshold_pk_challenge(inputs) } - /// Performs range checks on all secret witness values + /// Performs range checks on all secret witness values. + /// + /// These bounds are critical for BFV security-large coefficients would + /// break the scheme's hardness assumptions (Ring-LWE). fn perform_range_checks(self) { // Check that error polynomial has small coefficients self.eek.range_check_2bounds::(self.configs.eek_bound, self.configs.eek_bound); @@ -166,6 +180,14 @@ impl { /// CRT moduli: [q_0, q_1, ..., q_{L-1}] pub qis: [Field; L], @@ -27,21 +27,23 @@ impl Configs { } } -/// Threshold Share Decryption (Circuit 6). +/// Threshold share decryption (C6). /// -/// Verifies: -/// 1. Commitment to sk matches expected (from DKG decryption circuit) -/// 2. Commitment to e_sm matches expected (from DKG decryption circuit) -/// 3. Correct computation: d_i = c_0i + c_1i * s_i + e_i + r_2i * (X^N + 1) + r_1i * q_i +/// **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`. +/// +/// **Produces:** Truncated-`d` commitment for C7 (`decrypted_shares_aggregation`). pub struct ShareDecryption { /// Circuit parameters including bounds and cryptographic constants configs: Configs, - /// Expected commitment to aggregated sk shares (from DKG decryption circuit) + /// Expected commitment to aggregated sk shares (from C4 / protocol transcript) /// (public witness) expected_sk_commitment: Field, - /// Expected commitment to aggregated e_sm shares (from DKG decryption circuit) + /// Expected commitment to aggregated e_sm shares (from C4 / protocol transcript) /// (public witness) expected_e_sm_commitment: Field, @@ -171,8 +173,7 @@ impl Field { // Step 1: Verify sk commitment matches expected self.verify_agg_sk_commitment(); @@ -180,9 +181,9 @@ impl { pub qis: [Field; L], pub k0is: [Field; L], @@ -55,9 +56,17 @@ impl Configs { } } +/// User-data encryption ct0 (P3). +/// +/// **Role:** Prove the first ciphertext leg and publish `pk0`, `ct0`, `k1`, and `u` commitments +/// for the wrapper and ct1 circuit. +/// +/// **Consumes:** Threshold PK from C5 (`pk0is` witness path). +/// +/// **Produces:** `(pk0_commitment, ct0_commitment, k1_commitment, u_commitment)` public outputs. pub struct UserDataEncryptionCt0 { configs: Configs, - /// Public input from PkAggregation + /// Aggregated threshold `pk0` limb (from C5). pk0is: [Polynomial; L], ct0is: [Polynomial; L], u: Polynomial, @@ -182,14 +191,17 @@ impl (Field, Field, Field, Field) { + // Step 1: Witness bounds and CRT consistency of `e0`. self.check_range_bounds(); self.check_e0_crt_consistency(); + // Step 2: Domain-separated commitments for the FS transcript. let (pk0_commitment, ct0_commitment, k1_commitment, u_commitment) = self.generate_commitments(); + // Step 3: Fiat-Shamir challenge and batched evaluation check of ct0 equations. let gammas = self.generate_challenge(pk0_commitment, ct0_commitment, k1_commitment, u_commitment); diff --git a/circuits/lib/src/core/threshold/user_data_encryption_ct1.nr b/circuits/lib/src/core/threshold/user_data_encryption_ct1.nr index 2c84f1595d..4565d4e225 100644 --- a/circuits/lib/src/core/threshold/user_data_encryption_ct1.nr +++ b/circuits/lib/src/core/threshold/user_data_encryption_ct1.nr @@ -4,7 +4,10 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -//user data encryption for ct1- the second part of the ciphertext +//! User-data encryption - ct1 leg (P3). +//! +//! See `UserDataEncryptionCt1` for the circuit contract. + use crate::math::commitments::compute_multiple_polynomial_commitment; use crate::math::commitments::compute_single_polynomial_commitment; use crate::math::commitments::compute_user_data_encryption_ct1_challenge; @@ -14,6 +17,7 @@ use crate::math::commitments::DS_USER_DATA_ENCRYPTION_COMMITMENT; use crate::math::helpers::flatten; use crate::math::polynomial::Polynomial; +/// Parameters for user-data encryption ct1 (P3). pub struct Configs { pub qis: [Field; L], pub e1_bound: Field, @@ -33,11 +37,16 @@ impl Configs { } } +/// User-data encryption ct1 (P3). +/// +/// **Role:** Second ciphertext leg; `u` must match the ct0 `u_commitment`. +/// +/// **Produces:** `(pk1_commitment, ct1_commitment, u_commitment)` public outputs. pub struct UserDataEncryptionCt1 { configs: Configs, pk1is: [Polynomial; L], ct1is: [Polynomial; L], - /// Re-witnessed privately, checked against commit_u from Circuit A + /// Re-witnessed privately; checked against `u_commitment` from ct0 (same P3 flow). u: Polynomial, e1: Polynomial, p1is: [Polynomial<(2 * N) - 1>; L], @@ -130,12 +139,15 @@ impl (Field, Field, Field) { + // Step 1: Witness bounds. self.check_range_bounds(); + // Step 2: Commitments for the FS transcript. let (pk1_commitment, ct1_commitment, u_commitment) = self.generate_commitments(); + // Step 3: Fiat-Shamir challenge and evaluation check of ct1 equations. let gammas = self.generate_challenge(pk1_commitment, ct1_commitment, u_commitment); assert(self.verify_evaluations(gammas), "ct1 encryption check failed"); diff --git a/circuits/lib/src/lib.nr b/circuits/lib/src/lib.nr index 68f7aa2a96..aa4289ca99 100644 --- a/circuits/lib/src/lib.nr +++ b/circuits/lib/src/lib.nr @@ -4,13 +4,13 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -//! # lib - Enclave Zero-Knowledge Cryptographic Library -//! - **`core`**: Core functions and structs for Public Verifiable -//! Secret Sharing (PVSS) circuits implementations. -//! - **`math`**: Mathematical utilities including polynomial operations, SAFE sponge -//! API implementation, and helper functions for circuit construction. -//! - **`configs`**: Cryptographic parameter configurations for different security -//! levels (insecure, production) and different circuit variants (DKG, Threshold). +//! # lib - Interfold ZK library +//! - **`core`**: DKG and threshold TrBFV circuit logic (PV-TBFV / publicly verifiable +//! threshold BFV). +//! - **`math`**: Polynomials, SAFE sponge (Poseidon2 + Keccak per `safe.nr`), helpers, +//! `ModU128`, commitments. +//! - **`configs`**: Parameter presets (default -> secure / insecure / committee) for DKG +//! and threshold packages under `circuits/bin/`. pub mod math; pub mod core; diff --git a/docs/next.config.js b/docs/next.config.js index d4e87fa2dd..4f3ac8b89f 100644 --- a/docs/next.config.js +++ b/docs/next.config.js @@ -10,6 +10,7 @@ const nextra = require('nextra') const withNextra = nextra({ theme: 'nextra-theme-docs', themeConfig: './theme.config.jsx', + latex: true, }) module.exports = withNextra({ diff --git a/docs/package.json b/docs/package.json index fe7f08adb4..164ac761a7 100644 --- a/docs/package.json +++ b/docs/package.json @@ -10,6 +10,7 @@ "start": "next start" }, "dependencies": { + "katex": "^0.16.44", "next": "^14.2.1", "nextra": "^2.13.4", "nextra-theme-docs": "^2.13.4", diff --git a/docs/pages/_app.tsx b/docs/pages/_app.tsx index f57275885b..6873a1c71e 100644 --- a/docs/pages/_app.tsx +++ b/docs/pages/_app.tsx @@ -6,6 +6,7 @@ import React from 'react' import type { AppProps } from 'next/app' +import 'katex/dist/katex.min.css' import '../styles/globals.css' function App({ Component, pageProps }: AppProps) { diff --git a/docs/pages/_meta.json b/docs/pages/_meta.json index 94b37ba41d..959fcfcd09 100644 --- a/docs/pages/_meta.json +++ b/docs/pages/_meta.json @@ -19,6 +19,9 @@ "computation-flow": { "title": "E3 Computation Flow" }, + "cryptography": { + "title": "Cryptography" + }, "use-cases": { "title": "Use Cases" }, diff --git a/docs/pages/architecture-overview.mdx b/docs/pages/architecture-overview.mdx index fdf8951914..3065af7c03 100644 --- a/docs/pages/architecture-overview.mdx +++ b/docs/pages/architecture-overview.mdx @@ -157,3 +157,8 @@ As a developer, you'll interact with: - **Interfold smart contracts**: To submit computation requests and retrieve results. - **Compute Providers**: To run your E3P using verifiable or oracle-based systems. - **E3 Smart Contracts**: To verify the inputs and computation result. + +For how DKG, threshold BFV, and ZK circuits (**C0–C7**) fit the protocol story, see +[Cryptography](/cryptography). For the `circuits/` tree, scripts, and toolchain, see +[Noir Circuits](/noir-circuits) and the repo’s +[`circuits/README.md`](https://github.com/gnosisguild/enclave/blob/main/circuits/README.md). diff --git a/docs/pages/computation-flow.mdx b/docs/pages/computation-flow.mdx index 0e467994de..34d0181c68 100644 --- a/docs/pages/computation-flow.mdx +++ b/docs/pages/computation-flow.mdx @@ -118,6 +118,9 @@ event PlaintextOutputPublished(uint256 indexed e3Id, bytes plaintextOutput, byte Upon successful decryption, rewards are distributed to the active committee members. +**See also:** [Cryptography](/cryptography) (threshold BFV, phases P1–P4, circuit IDs) and +[Noir Circuits](/noir-circuits) (build, lint, `enclave noir`, Rust prover integration). + ### Failure Handling If any phase exceeds its deadline or a fault is detected, the E3 transitions to the `Failed` stage. diff --git a/docs/pages/cryptography.mdx b/docs/pages/cryptography.mdx new file mode 100644 index 0000000000..56cee522ce --- /dev/null +++ b/docs/pages/cryptography.mdx @@ -0,0 +1,391 @@ +--- +title: 'Cryptography' +description: + 'Threshold BFV, zero-knowledge circuits, and how they map to the Interfold implementation' +--- + +## Cryptography + +This page is a guided tour of the **cryptographic foundations** behind the Interfold: how +**threshold Brakerski–Fan–Vercauteren (BFV)** encryption, **distributed key generation (DKG)**, and +**zero-knowledge proofs** fit together so that ciphernodes can run an **E3** without any single +party holding the full decryption key, while still allowing **anyone** to check that keys, shares, +and decryptions were produced honestly. + +You do not need to read every section in order. If the execution model is still fuzzy, start with +[What is an E3?](/what-is-e3). When you are ready to compile circuits or inspect the tree, the +[Noir Circuits](./noir-circuits) page collects toolchain notes and repository layout. + +## Overview + +### Why threshold cryptography needs more than “honest majority” + +When many parties jointly generate keys or decrypt, classical threshold protocols spread trust +across participants: if enough of them behave, the protocol succeeds. That works well inside a +closed group, but it leaves **observers** with little to verify—they mostly assume the right nodes +ran the right steps. The Interfold design pushes in a different direction, often summarised as the +**coordination trilemma**: keep individual data and intermediate secrets confidential, avoid giving +any one party full control over decryption, and still make each cryptographic step **publicly +checkable** without re-running the whole computation. + +The protocol leans on **zero-knowledge proofs** for the “checkable” part and on **economics** (for +example slashing) so that detected misbehaviour is costly as well as visible. The aim is not to +replace engineering discipline, but to ensure that correctness rests on verifiable mathematics +rather than on blind trust in operators. + +### One execution, end to end + +Each confidential workload is an FHE program running inside an **E3**. After someone requests such a +computation, a committee of **N** [ciphernodes](https://blog.theinterfold.com/ciphernodes/) is drawn +using **sortition**, so membership is tied to verifiable randomness rather than to a central picker. +There is **no trusted dealer** handing out key material: every ciphernode contributes its own share +of the work and takes part in **PVDKG** (publicly verifiable DKG), producing proofs alongside +messages so that deviating parties can be identified and excluded without exposing other nodes’ +secrets. + +Write **A** for the **authorised** committee for that E3—initially all **N** members, then shrinking +as proofs fail and bad actors drop out of the active path. When DKG finishes, the network publishes +a **threshold public key** that serves as the reference for everyone who will encrypt inputs. The +numeric threshold **T** (how many partial decryptions you need later) is fixed at **program** level; +this page does not pin a single formula, because deployments can tune it. + +From there the story is easier to tell in order. Users encrypt under the threshold key. A **compute +provider** evaluates the FHE program on those ciphertexts; in practice that step usually ships with +its own **correct execution** story (for example attested FHE inside a zkVM), separate from the Noir +circuits that handle keys and decryptions. When the job is done, at least **T+1** honest members of +**A** publish **partial decryptions**, and an **aggregator** combines them into plaintext +**without** ever reassembling the full secret key in one place. + +### How the product phases line up + +It helps to name four **product phases**, P1 through P4, which reappear throughout the docs and in +the implementation. Think of them as chapters in the same book: DKG establishes the committee’s +keys, aggregation turns many contributions into one public key, users encrypt, then the system +decrypts after homomorphic evaluation. + +- **P1 — Distributed key generation** is the heavy chapter. Each ciphernode builds its **secret key + contribution** and matching **public key share**, splits the sensitive parts into **Shamir** + shares, and sends those shares encrypted under each peer’s **individual** BFV public key—because + shares must not travel in the clear. Parallel **smudging noise** material follows the same + pattern, since decryption later relies on noise that is also shared and proved. Proofs accompany + generation, share computation, encryption, and the local decrypt-and-aggregate step; as they fail + or succeed, the set **A** updates so that only honest nodes remain on the critical path. + +- **P2 — Public key aggregation** is deliberately short: someone acting as aggregator sums the + surviving public key shares into the single **threshold public key**. Circuit + [**C5**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_aggregation) + checks that this sum matches what earlier proofs committed to. + +- **P3 — User encryption** is where data providers enter. They encrypt under the aggregated + threshold key; the in-tree packages + [`user_data_encryption_ct0`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/user_data_encryption_ct0), + [`user_data_encryption_ct1`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/user_data_encryption_ct1), + and + [`user_data_encryption`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/user_data_encryption) + implement a **GRECO**-style proof pattern: show that the ciphertext is a valid BFV encryption + without revealing the message or the randomness. + +- **P4 — Threshold decryption** closes the loop after homomorphic evaluation. Ciphernodes in **A** + produce partial decryptions; circuits + [**C6**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/share_decryption) + and + [**C7**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/decrypted_shares_aggregation) + prove the shares and the final recombination. The FHE evaluation itself sits **between** P3 and + P4: it consumes user ciphertexts and yields the output ciphertext that **C6** and **C7** reason + about. Those evaluation proofs are **not** the same Noir binaries as DKG or decryption; what you + get depends on the FHE backend you plug in. + +### Wiring phases to packages in the repository + +Before any of this runs in production, a one-shot **`config`** proof (see +[`circuits/bin/config`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/config)) +checks that parameter presets—CRT limbs, noise bounds, Reed–Solomon parity matrices, and the +like—match across the deployment. After that, the table below is the map most people keep open while +reading the code: it connects the narrative phases P1–P4 to the **C0–C7** labels used in issues, +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/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. | + +### Sequence diagram + +The diagram below is the same story as a swimlane sketch: committee work, aggregation, users, +compute, then threshold decryption. It is not a substitute for the precise package list, but it +gives a sense of ordering when you read events on-chain or in logs. + +```mermaid +sequenceDiagram + participant CMT as Committee + participant AGG as Aggregator + participant USR as Users + participant CP as Compute Provider + participant BC as Blockchain + + Note over CMT,AGG: P1 — Distributed Key Generation + loop Each authorised node + CMT->>CMT: Individual sk/pk + proof (C0) + CMT->>CMT: Contribution + smudging + proofs (C1, C2) + CMT->>CMT: Encrypt and exchange shares + proofs (C3) + CMT->>CMT: Decrypt shares + proofs (C4) + end + + Note over CMT,AGG: P2 — Public Key Aggregation + CMT-->>AGG: pk shares + proofs + AGG-->>BC: Threshold public key + proof (C5) + + Note over USR,BC: P3 — User Encryption + USR-->>BC: Encrypted inputs + proofs + + BC-->>CP: Encrypted inputs + Note over CP: FHE program (off these circuits) + CP-->>BC: Encrypted output + execution attestation + + Note over CMT,AGG: P4 — Threshold Decryption + BC-->>CMT: Encrypted output + loop At least T+1 shares + CMT->>CMT: Partial decryption + proof (C6) + CMT-->>AGG: Share + proof + end + AGG-->>BC: Plaintext + proof (C7) +``` + +## Publicly verifiable threshold BFV (PV-TBFV) + +**PV-TBFV** means **publicly verifiable threshold Ring-BFV**: every sensitive step you would worry +about in a centralised system—generating key material, moving it under encryption, aggregating +public keys, taking user ciphertexts, emitting decryption shares—is backed by a statement that +verifiers can check **without** seeing the private witnesses. The DKG slice of that story is what we +call **PVDKG**; in the repository it lands in **C0** through **C4** (with +[**C1**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_generation) +living under +[`circuits/bin/threshold`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold) +and the rest of the DKG chain under +[`circuits/bin/dkg`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg)). + +Taken together, the design is aiming for a few properties that reinforce each other. **Threshold +security** means decrypting user data should require at least **T+1** cooperating honest shares for +the chosen **T**. **Public verifiability** means observers can validate each proof without +participating in the protocol. **Homomorphic evaluation** is what lets ciphertexts leave P3 and +enter the FHE engine before P4. **Robustness** means bad behaviour should show up as failing proofs, +so the protocol can update **A** and the economic layer can apply penalties when appropriate. + +### Commitments and why polynomials do not land on-chain whole + +BFV keys and intermediate polynomials are enormous under post-quantum parameters. Publishing them in +full for every step would be impractical on-chain, so each circuit hashes the relevant outputs into +a short **commitment** using the **SAFE** sponge (see +[`circuits/lib/src/math/safe.nr`](https://github.com/gnosisguild/enclave/blob/main/circuits/lib/src/math/safe.nr)), +built from **Poseidon2** and **Keccak256**. The story that emerges is a **chain of accountability**: +later circuits take earlier commitments as **public inputs**, re-hash the private witnesses inside +the proof, and check equality. That is how +[**C1**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_generation) +connects to +[**C5**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_aggregation), +and how [**C4**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_decryption) +hands off to +[**C6**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/share_decryption), +without ever dumping raw polynomials into calldata. Domain labels (`DS_*` in +[`commitments.nr`](https://github.com/gnosisguild/enclave/blob/main/circuits/lib/src/math/commitments.nr)) +keep different commitment kinds from being confused with one another. + +## Key terminology + +The same **BFV** ring arithmetic appears twice in the system: once for **individual** keys that +exist only to protect share traffic during DKG, and once for the **threshold** key that users and +decryption actually rely on. Mixing those two notions is the most common source of confusion, so the +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/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). | + +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/dkg/share_computation) ships +inside the secure preset that +[`config`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/config) checks. + +## Phases & Circuits + +The phase table in **Wiring phases to packages** is the map; this section walks the same path with +implementation detail. Names and paths match +[`circuits/README.md`](https://github.com/gnosisguild/enclave/blob/main/circuits/README.md). + +### P1 — Distributed Key Generation + +The DKG story begins by pinning down each node’s **individual** public key: circuit +[**C0**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/pk) (`dkg/pk`) commits +that key, and later +[**C3**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_encryption) proves +that share ciphertexts use exactly the key material promised in that commitment. + +Next, +[**C1**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_generation) +(`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/dkg/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 +shape below (the implementation names the error polynomial **eek**): + +```math +\begin{aligned} +\mathrm{pk}_0^{[\ell]}(\gamma) &= -a^{[\ell]}(\gamma)\,\mathrm{sk}(\gamma) + \mathrm{eek}(\gamma) + r_2^{[\ell]}(\gamma)(\gamma^N+1) + r_1^{[\ell]}(\gamma)\,q_\ell +\end{aligned} +``` + +(`pk_generation.nr`: **eek** is a **single** polynomial; **r1**, **r2** are per CRT limb; the second +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/dkg/share_computation) is not +a single monolithic proof: under +[`dkg/`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg) you will find the +recursive chain—base, chunk, batch, wrapper—that proves Shamir-style sharing and Reed–Solomon parity +on the contribution arrays while keeping proof size manageable. 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} +``` + +[**C3**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_encryption) +(`dkg/share_encryption`) then encrypts each share under the recipient’s individual key; in a fully +connected committee the proof count scales like **|A| × (|A| − 1)** per track. At a Fiat–Shamir +challenge point, the BFV encryption algebra it checks matches the usual two-leg ciphertext (per +limb), schematically: + +```math +\begin{aligned} +\mathsf{ct}_0^{[\ell]}(\gamma) &= \mathsf{pk}_0^{[\ell]}(\gamma)\,u(\gamma) + e_0^{[\ell]}(\gamma) + k_1(\gamma)\,k_0^{[\ell]} + r_1^{[\ell]}(\gamma)\,q_\ell + r_2^{[\ell]}(\gamma)(\gamma^N+1) \\ +\mathsf{ct}_1^{[\ell]}(\gamma) &= \mathsf{pk}_1^{[\ell]}(\gamma)\,u(\gamma) + e_1(\gamma) + p_1^{[\ell]}(\gamma)\,q_\ell + p_2^{[\ell]}(\gamma)(\gamma^N+1) +\end{aligned} +``` + +(`share_encryption.nr`: **k0**[\ell] are fixed scalars per limb; **e1** is one polynomial whose +evaluation is reused for every **ℓ** in the **ct1** leg, matching the witness layout in Noir.) + +Finally, +[**C4**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/dkg/share_decryption) +(`dkg/share_decryption`) runs after local decryption: it proves the opened values match **C2**’s +commitments, aggregates per modulus, and forwards commitments toward threshold decryption +([**C6**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/share_decryption)). +Because the threshold secret is the sum of contributions, each node combines the decrypted shares +into per-limb aggregates that line up with that design: + +```math +\mathrm{agg}[\ell][i] = \sum_{h \in \mathcal{H}} \mathsf{share}[h][\ell][i] \pmod{q_\ell} +``` + +### P2 — Public key aggregation + +[**C5**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/pk_aggregation) +(`threshold/pk_aggregation`) is the bridge from many committed contributions to one threshold public +key: it re-checks each contribution against **C1**, then aggregates. The **first** leg is summed +across honest parties; the **second** leg is the same CRS polynomial **a** for every party, so +**pk1_agg** is **not** a sum—it must equal each party’s **pk1** (see `verify_pk_for_basis` vs +`verify_pk1` in `pk_aggregation.nr`): + +```math +\begin{aligned} +\mathrm{pk}^{\mathrm{agg}}_{0}[\ell][i] &= \sum_{h \in \mathcal{H}} \mathrm{pk}_{0}[h][\ell][i] \pmod{q_\ell} \\ +\mathrm{pk}^{\mathrm{agg}}_{1}[\ell] &= \mathrm{pk}_{1}[h][\ell] = a^{[\ell]} \quad \forall\, h \in \mathcal{H} +\end{aligned} +``` + +### P3 — User encryption + +The user-encryption family proves that ciphertexts are valid BFV encryptions under the aggregated +key—implemented across +[`user_data_encryption_ct0`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/user_data_encryption_ct0), +[`user_data_encryption_ct1`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/user_data_encryption_ct1), +and +[`user_data_encryption`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/user_data_encryption)—following +a GRECO-style encryption proof pattern. Algebraically it is the same BFV encryption statement as in +**C3**, but with **pk0**, **pk1** taken from the aggregated threshold key committed by **C5**; the +split across `ct0` / `ct1` packages exists to keep witness sizes and verifier constraints +manageable. + +### P4 — Threshold decryption + +[**C6**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/share_decryption) +ties each partial decryption to **C4**’s aggregated material and to the homomorphic output +ciphertext. +[**C7**](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/threshold/decrypted_shares_aggregation) +collects at least **T+1** such shares, applies Lagrange weights, reconstructs across CRT, and +decodes modulo **t**; it runs once at the aggregator after enough valid **C6** proofs are available. + +**C6** proves a partial decryption share **d** built from the output ciphertext, the node’s Shamir +shares of the threshold secret and smudging noise (from **C4**), and reduction +quotients—schematically per CRT limb (see `verify_decryption_share_computation` in +`share_decryption.nr`): + +```math +d^{[\ell]}(\gamma) = \mathrm{ct}_0^{[\ell]}(\gamma) + \mathrm{ct}_1^{[\ell]}(\gamma)\,\mathrm{sk}^{[\ell]}(\gamma) + e_{\mathrm{sm}}^{[\ell]}(\gamma) + r_2^{[\ell]}(\gamma)(\gamma^N+1) + r_1^{[\ell]}(\gamma)\,q_\ell +``` + +**C7** first recombines partials with Lagrange coefficients at zero for the participating party +indices **x_i**, then stitches limbs and decodes to the plaintext ring. In short: + +```math +\begin{aligned} +L_i(0) &= \prod_{j \neq i} \frac{-x_j}{x_i - x_j} \pmod{q_\ell} \\ +u^{(\ell)} &= \sum_i d_i^{(\ell)}\,L_i(0) \pmod{q_\ell} +\end{aligned} +``` + +(`decrypted_shares_aggregation.nr` computes the same **L_i(0)** values with an equivalent +product–quotient form and explicit sign handling; **u**[\ell] is then formed coefficient-wise per +`compute_crt_components`.) + +CRT reconstruction checks **u**[\ell] + **r**[\ell] **q_ℓ** = `u_global` coefficient-wise, then +decoding uses the map in `verify_decoding` (BigNum mod **Q**, plaintext modulus **t**, and +`q_inverse_mod_t` as in `Configs`). + +## Footnotes + +Across the stack you will see the same few ideas reused in different algebraic costumes. +**Fiat–Shamir** turns interactive polynomial checks into single challenge points; +**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/dkg/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). +Finally, **range checks** keep coefficients inside the bounds that make RLWE-style assumptions +meaningful inside a proof. + +Some statements, especially the large **C2** pipeline, do not fit in one proof: the repository uses +inner UltraHonk proofs, wrapper circuits, and the folding machinery under +[`recursive_aggregation/`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/recursive_aggregation); +see [Noir Circuits](./noir-circuits) for how to build and verify them. + +## Where to go next + +If you are integrating proofs from Rust or following on-chain events, these entry points complement +this page: the +[`circuits/README.md`](https://github.com/gnosisguild/enclave/blob/main/circuits/README.md) index +for package names and **C0–C7** paths; +[DKG & computation](https://github.com/gnosisguild/enclave/blob/main/agent/flow-trace/04_DKG_AND_COMPUTATION.md) +for actors, gossip, and proof ordering in a running node. diff --git a/docs/pages/introduction.mdx b/docs/pages/introduction.mdx index 3dc845fe4b..83c20596a0 100644 --- a/docs/pages/introduction.mdx +++ b/docs/pages/introduction.mdx @@ -55,6 +55,10 @@ The Interfold coordinates encrypted execution using a combination of Fully Homom These components enable computation on encrypted inputs, verification of correct execution, and distributed control of decryption through the network of ciphernodes. +For a dedicated walkthrough of threshold BFV, PV-TBFV, and the circuit phases (C0–C7), see +[Cryptography](/cryptography). To compile and lint the Noir workspace, run `enclave noir`, and wire the +prover from Rust, see [Noir Circuits](/noir-circuits). + ### What You'll Learn This documentation covers everything you need to get started with the Interfold, including: diff --git a/docs/pages/noir-circuits.mdx b/docs/pages/noir-circuits.mdx index debf9381c0..0b59363f34 100644 --- a/docs/pages/noir-circuits.mdx +++ b/docs/pages/noir-circuits.mdx @@ -1,128 +1,105 @@ --- title: 'Noir Circuits' -description: 'Build and reuse Noir circuits for Interfold programs' +description: + 'Toolchain, repo layout, compile/lint scripts, and integration hooks for Interfold Noir circuits' --- -# Noir Circuits & Libraries +# Noir Circuits -The Interfold ships a dedicated Noir workspace under `circuits/` plus reusable libraries (SAFE -sponge API, polynomial utilities, modular arithmetic, PVSS circuit primitives, etc.). Use these -circuits as-is or vendor them into your own E3 programs. +This page is for **building and integrating** the Noir workspace under `circuits/`: where packages +live, which scripts to run, and how they connect to the Rust prover and on-chain verifiers. + +For **what** the circuits prove (PV-TBFV, phases **P1–P4**, **C0–C7**, commitments), read +[Cryptography](./cryptography) first. For **Rust types and events** (`ProofType`, `CircuitName`, +public inputs), use the repo’s +[`agent/flow-trace/04_DKG_AND_COMPUTATION.md`](https://github.com/gnosisguild/enclave/blob/main/agent/flow-trace/04_DKG_AND_COMPUTATION.md). + +The canonical **package index** (paths, **CircuitName**, C0–C7 IDs) is +[`circuits/README.md`](https://github.com/gnosisguild/enclave/blob/main/circuits/README.md) in the +open-source tree—Noir sources are the source of truth; treat docs as orientation only. ## Workspace layout -``` +```text circuits/ -├── lib/ -│ ├── Nargo.toml # library manifest +├── lib/ # Shared library (all `bin/` packages depend on this) │ └── src/ -│ ├── lib.nr # top-level library entry -│ ├── configs/ # BFV parameter configurations (default, insecure, secure, committee) -│ ├── core/ # DKG and threshold PVSS circuit implementations -│ │ ├── dkg/ # pk, share_computation, share_encryption, share_decryption -│ │ └── threshold/ # pk_generation, pk_aggregation, share_decryption, -│ │ # decrypted_shares_aggregation, user_data_encryption_ct0/ct1 -│ └── math/ # polynomial, SAFE sponge, modular arithmetic (U128), commitments +│ ├── lib.nr +│ ├── configs/ # BFV presets (default / secure / insecure / committee) +│ ├── core/dkg/ # C0, C2–C4 (core logic) +│ ├── core/threshold/ # C1, C5, P3, C6, C7 (core logic) +│ └── math/ # Polynomials, SAFE sponge, ModU128, commitments ├── bin/ -│ ├── config/ # configuration validation circuit -│ ├── dkg/ # DKG sub-circuits (pk, sk_share_computation, e_sm_share_computation, -│ │ # share_encryption, share_decryption) -│ ├── threshold/ # threshold sub-circuits (pk_generation, pk_aggregation, -│ │ # share_decryption, user_data_encryption_ct0/ct1, -│ │ # decrypted_shares_aggregation) -│ ├── insecure/ # legacy insecure-parameter circuits for development -│ ├── production/ # production-parameter circuits (placeholder) -│ └── recursive_aggregation/ # recursive proof aggregation -│ ├── fold/ # fold circuit (uses Aztec bb_proof_verification) -│ └── wrapper/ # recursive wrappers for DKG and threshold circuits -│ ├── dkg/ # pk, share_computation, share_encryption, share_decryption -│ └── threshold/ # pk_generation, pk_aggregation, share_decryption, -│ # decrypted_shares_aggregation, user_data_encryption -├── benchmarks/ # benchmark scripts and results -└── .gitignore +│ ├── 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}/ +└── benchmarks/ # Timing scripts and reports ``` -The core library at `circuits/lib/` contains reusable modules for polynomial arithmetic, the SAFE -sponge API, modular reduction helpers (including `U128` large-integer operations), and BFV -DKG/threshold PVSS primitives. Binary circuits under `circuits/bin/` implement specific proof -programs (DKG key generation, share encryption/decryption, threshold aggregation, recursive folding, -etc.). The library depends on `poseidon`, `keccak256` (from noir-lang), and `bignum` (from -gnosisguild/noir-bignum). +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/`. -To reference the library from an external project: +## Using `circuits/lib` from another Noir project + +Add a path or git dependency in your `Nargo.toml` (pin a **tag or rev** that matches your prover): ```toml [dependencies] enclave_lib = { git = "https://github.com/gnosisguild/enclave", tag = "v0.1.15", directory = "circuits/lib" } ``` -## Tooling requirements +Check `crates/zk-prover/Cargo.toml` / `versions.json` in the same release for compatible **nargo** +and **bb** versions. + +## Toolchain -Install the Noir toolchain: +Install Noir (or rely on CI / Docker): ```bash -curl -L https://noir-lang.org/install | bash # installs nargo + bb -nargo --version # v1.0.0-beta.16+ (see zk-prover Cargo.toml for exact tag) -bb --version # v3.0.0-nightly.20260102+ (see crates/zk-prover/versions.json) +curl -L https://noir-lang.org/install | bash # nargo + bb +nargo --version # match zk-prover / versions.json +bb --version ``` -> **Tip:** Use `enclave noir setup` instead of manual installation — it downloads and verifies the -> exact versions of `bb` and pre-compiled circuit artifacts needed by the Enclave ZK prover. - -### CLI ZK Prover Setup - -The Enclave CLI can manage the ZK prover toolchain (Barretenberg and circuits) for you: +**Recommended:** use the Enclave CLI so **bb** and artifacts match the prover: ```bash -# Check current ZK prover status (installed versions, paths) enclave noir status - -# Install or update the ZK prover components (bb binary, circuit artifacts) enclave noir setup - -# Force reinstall all components -enclave noir setup --force +enclave noir setup --force # reinstall ``` -`enclave noir status` shows installation paths, versions, and whether all components are present. -`enclave noir setup` downloads and installs the correct versions of `bb` and pre-compiled circuit -artifacts needed for proof generation. +## Compile, format, test -## Formatting & compilation - -Run the helper scripts from the repo root or reproduce them in your project: +From the **repository root**: ```bash -pnpm tsx scripts/build-circuits.ts # compile circuits, generate VKs, prepare release artifacts -./scripts/lint-circuits.sh # nargo fmt --check && nargo check across all circuit directories -./scripts/test-circuits.sh # run circuit unit tests (nargo test on circuits/lib) +pnpm tsx scripts/build-circuits.ts # compile, VKs, release artifacts (see script for scope) +./scripts/lint-circuits.sh # nargo fmt --check && nargo check (lib + bin areas) +./scripts/test-circuits.sh # nargo test on circuits/lib ``` -`lint-circuits.sh` checks formatting and validates circuits across `lib`, `bin/config`, -`bin/recursive_aggregation`, `bin/dkg`, and `bin/threshold`. Both shell scripts exit early (without -failing the whole build) if `nargo` is not installed, which makes CI happy when Noir is optional. +`lint-circuits.sh` and `test-circuits.sh` exit successfully when **nargo** is missing so +optional-Noir CI stays green. -## Exporting verifiers +## Solidity verifiers (example: CRISP) -Projects such as CRISP compile Noir circuits into Solidity verifiers using `bb`: +Projects such as CRISP compile circuits with **bb** and emit a verifier contract. Example +entrypoint: ```bash cd examples/CRISP ./scripts/compile_circuits.sh ``` -The script performs: - -1. Compiles enclave dependency circuits (`user_data_encryption_ct0`, `user_data_encryption_ct1`, and - the recursive wrapper for `user_data_encryption`) -2. Compiles the CRISP-specific circuit and fold circuit (recursive aggregation) -3. `bb write_vk` with `--oracle_hash keccak` on the fold circuit to emit the verification key -4. `bb write_solidity_verifier` to produce `CRISPVerifier.sol` from the fold VK -5. Copies the generated verifier into `packages/crisp-contracts/contracts/` -6. Replaces the license header and formats with Prettier +That flow typically: compile dependency circuits (`user_data_encryption_ct0` / `ct1` / wrapper), +compile the app-specific and fold circuits, `bb write_vk` (e.g. `--oracle_hash keccak` on the fold +target), `bb write_solidity_verifier`, then copy/format into the package contracts. Adapt paths for +your own fold root and output dir. -Adapt the script for your own circuits by changing the circuit paths and output directory. On-chain -verification uses the `ICircuitVerifier` interface: +On-chain verification matches the usual pattern: ```solidity interface ICircuitVerifier { @@ -133,14 +110,11 @@ interface ICircuitVerifier { } ``` -## Integrating with E3 programs +## Rust integration -- Import the library in your circuit's `Nargo.toml` (see the dependency snippet above) -- Generate Solidity verifiers and deploy them as `ICircuitVerifier` implementations -- Reference the compiled artifacts when deploying via the template or CRISP scripts -- For Rust-side proof generation, the `e3-zk-prover` crate handles witness generation, proof - generation (via `bb prove`), and verification (via `bb verify`) automatically +- **`e3-zk-prover`** (`crates/zk-prover`) — witness wiring, `bb prove` / `bb verify`, artifact paths + aligned with `enclave noir setup`. +- **Events / API** — `CircuitName`, `ProofType`, and request payloads are defined next to the node. -When adding new circuits, remember to update `package.json` scripts (e.g., `pnpm dev:all`) so they -call your compile script before spinning up the dev environment. This keeps your verifiers in sync -with the Noir source. +When you add or rename a circuit package, update root **`package.json`** / CI scripts that call +`build-circuits.ts` or `lint-circuits.sh` so dev and deployment stay in sync with Noir sources. diff --git a/docs/pages/what-is-e3.mdx b/docs/pages/what-is-e3.mdx index ad24fa301e..19947be596 100644 --- a/docs/pages/what-is-e3.mdx +++ b/docs/pages/what-is-e3.mdx @@ -15,6 +15,9 @@ confidentiality and support correct execution: Together, these components make it possible to compute across private inputs without exposing the underlying data. +The docs site goes deeper on how the ciphernode stack implements this in practice (BFV, DKG, proofs +**C0–C7**) in [Cryptography](/cryptography); tooling lives under [Noir Circuits](/noir-circuits). + ### When to Use E3s E3s are useful when you need to: diff --git a/packages/enclave-contracts/test/Pricing/Pricing.spec.ts b/packages/enclave-contracts/test/Pricing/Pricing.spec.ts index 18910e562c..c773286b65 100644 --- a/packages/enclave-contracts/test/Pricing/Pricing.spec.ts +++ b/packages/enclave-contracts/test/Pricing/Pricing.spec.ts @@ -25,8 +25,6 @@ import { Enclave__factory as EnclaveFactory, MockUSDC__factory as MockUSDCFactory, } from "../../types"; -import type { Enclave } from "../../types/contracts/Enclave"; -import type { MockUSDC } from "../../types/contracts/test/MockStableToken.sol/MockUSDC"; import { encodePkProof } from "../fixtures"; const { ethers, ignition, networkHelpers } = await network.connect(); @@ -346,20 +344,6 @@ describe("E3 Pricing", function () { }; }; - // Helper to make a request - const _makeRequest = async ( - enclave: Enclave, - usdcToken: MockUSDC, - requestParams: Parameters[0], - signer?: Signer, - ) => { - const fee = await enclave.getE3Quote(requestParams); - const tokenContract = signer ? usdcToken.connect(signer) : usdcToken; - const enclaveContract = signer ? enclave.connect(signer) : enclave; - await tokenContract.approve(await enclave.getAddress(), fee); - return enclaveContract.request(requestParams); - }; - // ────────────────────────────────────────────────────────────────────────── // getE3Quote() — Parametric Fee Calculation // ────────────────────────────────────────────────────────────────────────── @@ -784,16 +768,7 @@ describe("E3 Pricing", function () { describe("End-to-end request with parametric pricing", function () { it("charges the computed fee and completes successfully", async function () { - const { - enclave, - usdcToken, - request, - // ciphernodeRegistryContract, - // operator1, - // operator2, - // operator3, - owner, - } = await loadFixture(setup); + const { enclave, usdcToken, request, owner } = await loadFixture(setup); const fee = await enclave.getE3Quote(request); const ownerAddr = await owner.getAddress(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1cd2bacd24..881873a5a2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -72,6 +72,9 @@ importers: docs: dependencies: + katex: + specifier: ^0.16.44 + version: 0.16.44 next: specifier: ^14.2.1 version: 14.2.33(@playwright/test@1.52.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -728,7 +731,7 @@ importers: version: 5.3.0 '@risc0/ethereum': specifier: file:lib/risc0-ethereum - version: risc0-ethereum@file:templates/default/lib/risc0-ethereum + version: file:templates/default/lib/risc0-ethereum '@types/chai': specifier: ^4.2.0 version: 4.3.20 @@ -3100,6 +3103,9 @@ packages: '@reown/appkit@1.7.8': resolution: {integrity: sha512-51kTleozhA618T1UvMghkhKfaPcc9JlKwLJ5uV+riHyvSoWPKPRIa5A6M1Wano5puNyW0s3fwywhyqTHSilkaA==} + '@risc0/ethereum@file:templates/default/lib/risc0-ethereum': + resolution: {directory: templates/default/lib/risc0-ethereum, type: directory} + '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} @@ -7013,8 +7019,8 @@ packages: jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} - katex@0.16.25: - resolution: {integrity: sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==} + katex@0.16.44: + resolution: {integrity: sha512-EkxoDTk8ufHqHlf9QxGwcxeLkWRR3iOuYfRpfORgYfqc8s13bgb+YtRY59NK5ZpRaCwq1kqA6a5lpX8C/eLphQ==} hasBin: true keccak@3.0.4: @@ -8783,9 +8789,6 @@ packages: resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} engines: {node: '>= 0.8'} - risc0-ethereum@file:templates/default/lib/risc0-ethereum: - resolution: {directory: templates/default/lib/risc0-ethereum, type: directory} - robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} @@ -13241,6 +13244,8 @@ snapshots: - utf-8-validate - zod + '@risc0/ethereum@file:templates/default/lib/risc0-ethereum': {} + '@rolldown/pluginutils@1.0.0-beta.27': {} '@rollup/plugin-inject@5.0.5(rollup@4.52.5)': @@ -18683,7 +18688,7 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 - katex@0.16.25: + katex@0.16.44: dependencies: commander: 8.3.0 @@ -19180,7 +19185,7 @@ snapshots: dayjs: 1.11.19 dompurify: 3.3.0 elkjs: 0.9.3 - katex: 0.16.25 + katex: 0.16.44 khroma: 2.1.0 lodash-es: 4.17.21 mdast-util-from-markdown: 1.3.1 @@ -19303,7 +19308,7 @@ snapshots: micromark-extension-math@2.1.2: dependencies: '@types/katex': 0.16.7 - katex: 0.16.25 + katex: 0.16.44 micromark-factory-space: 1.1.0 micromark-util-character: 1.2.0 micromark-util-symbol: 1.1.0 @@ -19879,7 +19884,7 @@ snapshots: github-slugger: 2.0.0 graceful-fs: 4.2.11 gray-matter: 4.0.3 - katex: 0.16.25 + katex: 0.16.44 lodash.get: 4.4.2 next: 14.2.33(@playwright/test@1.52.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-mdx-remote: 4.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -20112,7 +20117,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.1(typescript@5.8.3)(zod@3.22.4) + abitype: 1.1.1(typescript@5.8.3)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.8.3 @@ -20809,7 +20814,7 @@ snapshots: '@types/katex': 0.16.7 hast-util-from-html-isomorphic: 2.0.0 hast-util-to-text: 4.0.2 - katex: 0.16.25 + katex: 0.16.44 unist-util-visit-parents: 6.0.2 vfile: 6.0.3 @@ -20966,8 +20971,6 @@ snapshots: hash-base: 3.1.2 inherits: 2.0.4 - risc0-ethereum@file:templates/default/lib/risc0-ethereum: {} - robust-predicates@3.0.2: {} rollup@4.52.5: @@ -22168,7 +22171,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.8.3)(zod@3.22.4) + abitype: 1.1.0(typescript@5.8.3)(zod@3.25.76) isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) ox: 0.9.6(typescript@5.8.3) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)