diff --git a/Cargo.lock b/Cargo.lock index aade891fb8..48b8666def 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3065,6 +3065,7 @@ dependencies = [ "e3-trbfv", "e3-utils", "e3-zk-helpers", + "e3-zk-prover", "fhe-math", "futures", "num-bigint", @@ -3937,6 +3938,7 @@ dependencies = [ "e3-config", "e3-data", "e3-events", + "e3-evm", "e3-fhe-params", "e3-polynomial", "e3-request", diff --git a/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md b/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md index d80277dba2..e5d9481e81 100644 --- a/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md +++ b/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md @@ -331,13 +331,20 @@ ProofFailureAccusation arrives via P2P from another committee member │ ├─ 1. Verify accuser is a committee member │ -├─ 2. Verify accuser's ECDSA signature on accusation digest +├─ 2. Validate accusation deadline against local policy: +│ - reject if deadline <= now (expired) +│ - reject if deadline > now + accusationVoteValidity + skew +│ - reject all peer accusations when accusationVoteValidity == 0 +│ - `skew` defaults to 30s and is configurable via +│ `ACCUSATION_DEADLINE_SKEW_SECS` on the node process │ -├─ 3. Compute accusation_id: +├─ 3. Verify accuser's ECDSA signature on accusation digest +│ +├─ 4. Compute accusation_id: │ keccak256(abi.encodePacked(chainId, e3Id, accused, proofType)) │ → Deterministic: all nodes compute same ID for same accusation │ -├─ 4. Determine own vote based on local verification cache: +├─ 5. Determine own vote based on local verification cache: │ │ │ ├─ Case A: We already FAILED verification for (accused, proof_type): │ │ → Vote agrees = true @@ -351,7 +358,7 @@ ProofFailureAccusation arrives via P2P from another committee member │ │ → Vote after re-verification completes │ └─ For other proofs: vote agrees = false (no local evidence) │ -├─ 5. Create and SIGN vote: +├─ 6. Create and SIGN vote: │ AccusationVote { │ e3_id, accusation_id, voter: my_address, │ agrees: , data_hash, @@ -359,7 +366,7 @@ ProofFailureAccusation arrives via P2P from another committee member │ } │ → Broadcast via P2P gossip │ -└─ 6. Check quorum immediately +└─ 7. Check quorum immediately ``` #### Step 3: Vote Digest & Accusation ID (Must Match Solidity) diff --git a/circuits/.gitignore b/circuits/.gitignore index 68eca2b539..5c2ed3bb00 100644 --- a/circuits/.gitignore +++ b/circuits/.gitignore @@ -1,2 +1,4 @@ target/ -Prover.toml \ No newline at end of file +Prover.toml +# Which BFV preset last populated circuits/bin/target/ (written by build-circuits hydrate/build). +.active-preset.json \ No newline at end of file diff --git a/circuits/benchmarks/README.md b/circuits/benchmarks/README.md index 9d3a110091..5fe45048e7 100644 --- a/circuits/benchmarks/README.md +++ b/circuits/benchmarks/README.md @@ -19,31 +19,71 @@ From this directory: Options and secure-only **config** circuit behavior are documented in the script and `config.json`. +### Proof aggregation and folding (integration) + +The gas / integration stage runs `cargo test -p e3-tests test_trbfv_actor` with **proof aggregation +enabled by default** (`E3Requested.proof_aggregation_enabled = true`): per-node `ZkNodeDkgFold`, +fold attestations (EIP-712 against `DkgFoldAttestationVerifier`), and exported folded +`dkg_aggregator` / `decryption_aggregator` proofs for Π_DKG / Π_dec on-chain gas. + +| Flag / env | Effect | +| ------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| `--proof-aggregation on` (default) | Full fold + aggregator path; folded artifacts in report | +| `--proof-aggregation off` / `--no-proof-aggregation` | Baseline without node folds / folded export | +| `BENCHMARK_PROOF_AGGREGATION` | Same as above when calling `extract_crisp_verify_gas.sh` directly | +| `BENCHMARK_MULTITHREAD_JOBS=N` / `--multithread-jobs N` | Rayon concurrent ZK jobs (default `1`) | +| `BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER=0x…` | EIP-712 verifying contract for fold attestations (default: localhost deploy address) | + +**Default output directories** (under `circuits/benchmarks/`; aggregation on/off no longer +overwrites the same folder): + +| Mode | Proof aggregation on (default) | Proof aggregation off (`--no-proof-aggregation`) | +| ---------- | ------------------------------ | ------------------------------------------------ | +| `insecure` | `results_insecure_agg/` | `results_insecure_no_agg/` | +| `secure` | `results_secure_agg/` | `results_secure_no_agg/` | + +**A/B comparison** (from `circuits/benchmarks`): + +```bash +./run_benchmarks.sh --mode insecure --no-proof-aggregation +./run_benchmarks.sh --mode insecure +# Compare results_insecure_no_agg/report.md vs results_insecure_agg/report.md +``` + +`report.md` includes **Audit status**, **Measurement methodology** (metric kinds), labeled **Role / +Phase** rows (`wall_clock` vs `isolated_nargo` vs `tracked_job_wall`), **NodeDkgFold sub-steps**, +and **Folded on-chain artifacts** when `integration_summary` is present. Verify gas must be complete +(not N/A) for audit sign-off. + ### What gets stored (secure / insecure) -A full `./run_benchmarks.sh --mode ` run writes: +A full `./run_benchmarks.sh --mode ` run writes to `results__agg/` or +`results__no_agg/` (see table above): + +- `raw/*.json` — Nargo timing + artifact sizes (source for the **Circuit Benchmarks** table) +- `crisp_verify_gas.json` — verify gas, calldata gas, artifact sizes, and **`integration_summary`** + from `test_trbfv_actor` when gas extraction succeeds +- `integration_summary.json` — snapshot of `integration_summary` (phase timings, folded proofs, + multithread / operation timings) +- `benchmark_run_meta.json` — CLI flags (mode, proof aggregation, multithread jobs, verbose) +- `report.md` — rendered summary of all of the above -- `results_/raw/*.json` — Nargo timing + artifact sizes (source for the **Circuit Benchmarks** - table) -- `results_/crisp_verify_gas.json` — verify gas, calldata gas, artifact sizes, and - **`integration_summary`** from `test_trbfv_actor` when gas extraction succeeds -- `results_/integration_summary.json` — snapshot of `.integration_summary` (phase timings, - folded proofs, **multithread / operation_timings** after a fresh integration export) -- `results_/report.md` — rendered summary of all of the above +Older runs used `results_/` without the `_agg` / `_no_agg` suffix; `regenerate_report.sh` +still finds that layout as a fallback. ### Regenerate `report.md` only (no integration re-run) From this directory, after you already have `raw/` + `crisp_verify_gas.json`: ```bash -./regenerate_report.sh ./regenerate_report.sh --mode insecure +./regenerate_report.sh --mode insecure --no-proof-aggregation ``` `crisp_verify_gas.json` embeds the integration timings; if you also keep `integration_summary.json` in the same folder, the script passes it explicitly (useful when gas JSON is missing a field but the snapshot is complete). `regenerate_report.sh` itself does not re-run `test_trbfv_actor`; it renders -from `results_/raw`, `crisp_verify_gas.json`, and (optionally) `integration_summary.json`. +from the matching `results__{agg|no_agg}/` directory. ## Refresh after parameter changes @@ -60,13 +100,13 @@ pnpm -C examples/CRISP/packages/crisp-sdk build # Extract on-chain verify gas from simulated verifier tests ./circuits/benchmarks/scripts/extract_crisp_verify_gas.sh \ - --output "./circuits/benchmarks/results_insecure/crisp_verify_gas.json" + --output "./circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json" # Regenerate report with gas values ./circuits/benchmarks/scripts/generate_report.sh \ - --input-dir "./circuits/benchmarks/results_insecure/raw" \ - --output "./circuits/benchmarks/results_insecure/report.md" \ - --gas-json "./circuits/benchmarks/results_insecure/crisp_verify_gas.json" + --input-dir "./circuits/benchmarks/results_insecure_agg/raw" \ + --output "./circuits/benchmarks/results_insecure_agg/report.md" \ + --gas-json "./circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json" ``` If Π_DKG / Π_dec **verify gas** is `N/A` because `crisp_verify_gas.json` came from a failed extract, @@ -77,7 +117,7 @@ step and merge **dkg** / **dec** into the gas file (no Rust re-run): # For secure folded proofs, align Solidity verifiers first (--build may take a while). ./circuits/benchmarks/scripts/replay_folded_verify_gas.sh \ --summary "/tmp/summary_secure.json" \ - --gas-json "./circuits/benchmarks/results_secure/crisp_verify_gas.json" \ + --gas-json "./circuits/benchmarks/results_secure_agg/crisp_verify_gas.json" \ --build secure-8192 ``` @@ -87,13 +127,13 @@ If `crisp_verify_gas.json` has `integration_summary: null` but you still have th ```bash ./circuits/benchmarks/scripts/generate_report.sh \ - --input-dir "./circuits/benchmarks/results_secure/raw" \ - --output "./circuits/benchmarks/results_secure/report.md" \ - --gas-json "./circuits/benchmarks/results_secure/crisp_verify_gas.json" \ + --input-dir "./circuits/benchmarks/results_secure_agg/raw" \ + --output "./circuits/benchmarks/results_secure_agg/report.md" \ + --gas-json "./circuits/benchmarks/results_secure_agg/crisp_verify_gas.json" \ --integration-summary "/tmp/summary_secure.json" ``` -For secure mode, use `--mode secure` and replace `results_insecure` with `results_secure`. +For secure mode, use `--mode secure` and the `results_secure_{agg|no_agg}/` directories. ## Reported protocol tables @@ -107,6 +147,9 @@ For secure mode, use `--mode secure` and replace `results_insecure` with `result - an `Integration test` section (end-to-end phase wall-clock timings) - a `Thread pool` section (Rayon threads / cores) - `CPU-bound operation timings` (tracked in-process averages/totals) + - `Proof aggregation / folding` (enabled flag, fold attestation verifier address) + - `Aggregation / fold operation timings` (`ZkNodeDkgFold`, `ZkDkgAggregation`, etc.) + - `Folded on-chain artifacts` (byte sizes used for Π_DKG / Π_dec gas replay) ## Derivation rules @@ -136,10 +179,19 @@ EVM verifier `estimateGas` in `packages/enclave-contracts/scripts/benchmarkGasFr `extract_crisp_verify_gas.sh` (and `replay_folded_verify_gas.sh --build `) call `ensure_circuit_preset_built.sh`, which runs -`pnpm build:circuits --skip-if-built --no-clean --no-clean-targets` by default (skips recompile when -`dist/circuits//.build-stamp.json` and marker artifacts match the current circuit sources). -Then `pnpm generate:verifiers --no-compile` refreshes Honk contracts before integration export and -Hardhat replay. +`pnpm build:circuits --skip-if-built --no-clean --no-clean-targets` by default. When +`dist/circuits//` is already built but `circuits/bin/` still reflects another preset (e.g. +you ran insecure benchmarks after a secure build), the build script **hydrates** `circuits/bin` from +`dist/` in seconds instead of recompiling (~50 minutes for `secure-8192`). You only pay the full +compile once per preset until circuit sources change. Then +`pnpm generate:verifiers --check --no-compile --preset ` verifies that +`dist/circuits//` is built and `circuits/bin/.active-preset.json` matches the benchmark mode +(`insecure-512` for `--mode insecure`, `secure-8192` for `--mode secure`). For **insecure** runs it +also diffs the committed Honk Solidity verifiers (`DkgAggregatorVerifier.sol`, +`DecryptionAggregatorVerifier.sol`) against the current insecure VKs. For **secure** runs it skips +that `.sol` diff (committed verifiers stay pinned to `insecure-512` for production deploy); gas +replay deploys fresh aggregator verifiers from `circuits/bin` at runtime. If you see preset mismatch +or insecure drift errors, follow the fix recipe printed by the script. - **`--force-build`** on extract/replay/ensure: full rebuild (same as a fresh `build:circuits`). - **`--skip-build`** on extract/replay: skip circuit build and Honk generation (only re-run diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json deleted file mode 100644 index ee6f0369ae..0000000000 --- a/circuits/benchmarks/results_insecure/crisp_verify_gas.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "verify_gas": { - "dkg": 3042430, - "user": 2972893, - "dec": 3553544 - }, - "source": "folded_proof_export_plus_crisp_verify_test", - "artifact_sizes_bytes": { - "dkg": { - "proof": 10944, - "public_inputs": 480 - }, - "dec": { - "proof": 10944, - "public_inputs": 3552 - } - }, - "calldata_gas": { - "dkg": { - "proof": 169968, - "public_inputs": 6144, - "total": 176112 - }, - "dec": { - "proof": 169848, - "public_inputs": 17304, - "total": 187152 - } - }, - "integration_summary": { - "integration_test": "test_trbfv_actor", - "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 1, "cores_available": 14 }, - "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.111521499, "runs": 3, "total_seconds": 0.334564499 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.610321888, "runs": 3, "total_seconds": 1.830965666 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.559050209, "runs": 1, "total_seconds": 0.559050209 }, - { "name": "GenEsiSss", "avg_seconds": 0.124032833, "runs": 3, "total_seconds": 0.372098501 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.226692527, "runs": 3, "total_seconds": 0.680077583 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.500284375, "runs": 1, "total_seconds": 8.500284375 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 49.366586083, "runs": 1, "total_seconds": 49.366586083 }, - { "name": "ZkDkgAggregation", "avg_seconds": 21.116986167, "runs": 1, "total_seconds": 21.116986167 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 1.465883083, "runs": 6, "total_seconds": 8.795298501 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 62.328322972, "runs": 3, "total_seconds": 186.984968916 }, - { "name": "ZkPkAggregation", "avg_seconds": 2.200691667, "runs": 1, "total_seconds": 2.200691667 }, - { "name": "ZkPkBfv", "avg_seconds": 0.336088264, "runs": 3, "total_seconds": 1.008264792 }, - { "name": "ZkPkGeneration", "avg_seconds": 1.351367042, "runs": 3, "total_seconds": 4.054101126 }, - { "name": "ZkShareComputation", "avg_seconds": 2.682164854, "runs": 6, "total_seconds": 16.092989126 }, - { "name": "ZkShareEncryption", "avg_seconds": 2.506225536, "runs": 24, "total_seconds": 60.149412873 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.176445291, "runs": 3, "total_seconds": 18.529335875 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.100550749, "runs": 3, "total_seconds": 0.301652249 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.221828033, "runs": 5, "total_seconds": 1.109140168 } - ], - "operation_timings_total_seconds": 381.986468376, - "timings_seconds": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.0715085 }, - { "label": "Committee Setup Completed", "seconds": 20.22829075 }, - { "label": "Committee Finalization Complete", "seconds": 0.006707541 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 304.143739958 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 306.698077958 }, - { "label": "Application CT Gen", "seconds": 0.313315125 }, - { "label": "Running FHE Application", "seconds": 0.003688125 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 79.884513625 }, - { "label": "Entire Test", "seconds": 410.2113715 } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000001104c69c76ea8f5f6000000000000000000000000000000000000000000000005c85075d6baa615c900000000000000000000000000000000000000000000000eb6de0b1c015f0f2a0000000000000000000000000000000000000000000000000002a0b5c2857c800000000000000000000000000000000000000000000000036176084b0205127a000000000000000000000000000000000000000000000004549377f025fd0f40000000000000000000000000000000000000000000000003ff07c8a94fbdbbd8000000000000000000000000000000000000000000000000000185ecac0421330000000000000000000000000000000000000000000000044f1f17a53eba29b1000000000000000000000000000000000000000000000004096147c1d40da636000000000000000000000000000000000000000000000007c2eaade9aae157c100000000000000000000000000000000000000000000000000023ccf2dca92b000000000000000000000000000000000000000000000000e02d12f90edb8cac6000000000000000000000000000000000000000000000007c223713eba54496600000000000000000000000000000000000000000000000ec928a9ef7dcf2da9000000000000000000000000000000000000000000000000000282e9bb83bee61a05d33c39ba3812376a494cad4c46e07e18763a4c0cc37c7ac2f3635530c6110f00693d937276e524dafd5fffac373d40ca9ac38bc4003aa97fa25c28eaae471e6801e127832204ea5a325f05bb8550bd90338e17663499f9908e07c1a27a4d25db068d58486c5ed1d571b154613b2a444f9ef333d6490c341531f43898470028e20e0d46d87d6e6a8c672bd88dbdbae7c0e0a9d06da0323fdaf7ee61ceb7611d45afd3c8ec55431dab1b693939c2a7f313b832a28959c3ae3d30629655cc051ff1a5efe96161c60a46f240cb6ca1a531353aec9b48b52ad525bf6481f17e0923676a74fc00629f08348ca4b4467e32b7907ccf75e78538096134b580c00ad016d8f24d8b2251a384465ecf7da74301f64008beb9c8b71af609a0085afa8bb10891080d365806d9ed9f21901108254290c057a4b986687bebd3eae86d9d84d52ba59894f06a73d164ff99fcb0d13873279ce4f307eef63d2c0ba38c4624e95e009c113dba40ba429e2620e10806283dd5fad8df1b6a85051c1f7167194c70620f370484b9bd06cf6e6199ccd69d2c0e0db004474b4ffc62084f7697d21fc04f1518380c30df7edd6752046611f209863dd69ccbd4af39b35e05dbaa35d7aea81dac6609d0b9b649962dd23bd220709758af7743abf5395724f809100dfd19cb13ee772bc80efdcb985314bbe58feda8ac8cb79523fe9ff183ebf3e6835eff81084ebea45358443421fb0f2d31a11171c0537eaf216aa68a5bfefa0d02b1c5b92a10ac9238f294ee18007a6f18f72214951c398ce84c3616bcf3c79e0f2e2f022a0c90527061c47a42c43cace4fc879a5a326863a6f82d9c6bb420290aa900d8244a98b1668bcfcb6023d101fde9f2fa579bbd8947480f7058466d6000b38c5c1a4fd3aa5794aad136c957e92be80cca60120b6d839e54f1036f9e857e14746f2433e79c2a2df9f7ea53e60405fe812eb086dfac1f0b0c638610dbdd208a28f2260c558292be011ce1ad7ea6fc1a18428a3199764be0e5e4169e261b655ffddd06ca1104a26cac71683ee832dcd3cac2ca05e81de30bed5f8b67ce620c6f88f62d90a64a9659cd90458edbd62bb6cc9f8d942669ccb243386ee53c938746a2462ab0830a5ee749ccf6259eb0ddd308297e3093c1e2bf8454dd5e900345907a8608b9832d8e40d08cb3dbf14939398e8c032b1baf82f3b45da26fc2aa8a69639d1379060a52801809840916499ab8679e0af7fa2b5e549f58ddfc8c60090ad5d42fcf59ab9661b29f6aee13a20e279602925e6fffb2328ef1334585e819ea8002085571a91540444e60b8c9350331f5cdfe8ba889466f4b26343d3b179b436d1015fdbeb6b1586fec95a850abe17e3d23dbb519b068c9c59ed740833e71319db516be4631e64a10edbbdfac8daafe9004a1dfb5631249e558132163eb6429c715143679b05e2edc9c236a9a7ca760761c3a117bb03755bf8d04aefc7742ab0ba811710087242ad211047ff4f6b9c4f8faa4640f53721a400815c301a8b079d1ca2b7184bc65e7bc3cd2c380df2376ec233411c4d429fb12795cdaa8b932bff94628703868796573c773ba5a60dbe2dcfdba86fbf73e2cbabe306511dd6f71509607ebc04687500585f37d8135ebac5386803439c01b36e449d3b029c1576fc2f422160f4fa03d8f11c89e134103a7918939563aad5285049c66b3479d416852b11896f8b31c6d91580c0cc03bd04ef40a9b201d686cbfec6170ee5a1aace7c7f2248c548038c030a7c267fe72915cd397e273c42561e11410672a1e41f71827eb247b679c4eebf11bf064f256f418ef2364b7eaa29fa9cfbf018eef1952daebd915d423652908cbd25b28b3b07d28cc918af5ac73226b02d6340d02ee74f643db15c870a4de33c870580fb55142ed86a7b1efb02d4f5d1d6e074137126f61818d0dce8e49b8aff13f2e80fa7d585aff6147ee7265bc92df749635d9cd77bfb87508e4dd08b4f437bb59702e34ec8c390bf6e7455a4619bb3586688b16e1045a9c0c8d0b760cf3d11357e9840fd3e702cce25045c7835bb1b0ce6275af5571d18d1e90731123ccd8bb5b7550b8ba901f3e94958acb3e9583067cdaf68cd913726817755888db2a7a28e1a4748b9ede248fe0ef76aa51bf8e5eaca3049ac9dd7984241212bdbe5b3d0d4b586a7f468056893fbdde4c94203e8ff8238fa1a028ab2b059669f73b36030f5205fcc5958017fd26f9fa8d827b6cf11aee31fa922e8f0c21c5cccf059ac7d29e54a4df6f1e43b8a4b22689859e8569dc98990b9fd1d1b2222e19e76cbb31fff18fcf40798cf42482bc063795fa19dfe65eb6e9a570b4c41e85f9c94ff56701adabc0cdb76def2f5770270aa236e1be1c18ed64e51cdc911a1133e096a0272e0675c680fe24f2cb4f9a201401977945b920b02e60c84f3309e8ba8688c6e982bc9357327d438c5d34986a3ee5c88fc5886a4c66f1d14ef625d0aa01c82cf320cb1543887eea14b4481733f213beaef47ad92251eb2910bb1c23b4c930200102e6ec848b5470cc88a0204f6d4a9aaa1cb29e808e3cf2cc7622ea3e6a331b26e1d2a78f90b99066780df63afffc89bb080ef954828fc64a9b0e589c3d02f1f8f8c03919178dcd7138e1381f54749a684c6c7f852b0e85251427b6dad8334cd11f9f2e16ab2d7654d1dad887e5dce9d872887c6dc9d07f45481835c2ae47a1c0c9b680a009ce8218506f77c3b8b62654b8fb93043f60dca8992c4ca79d52dc8431a240cc5d01a027438fa19c91f638b7cddd6db1a1f6faedbe1c7eda2cda8637e7e0a2f49d2ad0109f7a5bbbadf6c8478ba78e716fab26944e182e437f00afe8873f1030439bfed6f6bb3abc39bd1882eb2b6eda0078c680e924d942cd5b55449783c3a8f192e24aaa6c305167959fe1cf58e9065a7784325c2d0fe06c3e064f99ebc1570a82cd43c33ea443971e0f434ac11883fe84461b9821746609c618a3362bf051334253a22ae38e9b6773a528204c0797297835f3f61549c8cd8ad16bb415ada181c52d843d558dea5cd548c3823ac4e3d6df1838e0073102582f62d7dc0ac66710e9d0e8826ef77755403b7f12a2863faac6dfe4cb1914e29db1b956406e9f46ba2653617ac8ede05dfb69035dca3ee5c2d00d3a612ec25b4fe71e06659ef23bb53a839b6ee48c4e38207109e68d3e073fef16eb062dcc3a22a15afb381581cd7e9db2c78f2b801ef7104a5ffb41dce92caeebf6141190682aae5ce74e6b6adc7c901eb6db76c548746b9170a1c81930149032b19c0c0ab947223f7d82d1f7fe5930eb75cb6dd0bf5394a906376146da02731747ba17ee40b5061140ddbffbf7b01d0e6661cb6d3cca9eeca0a0c4640c5d61c72d9819f1b54c8878b1cacc853f1ca2e3ef455bd082bf5bf60e4b13ba7569e7804d5b1442193595af32b92e91b90c7f7c9999f7c665a9c7a7f5911d73d415c3339c6f0a968a68b6475c1dabe40dd46087630e1fabf2155f2017e4fb42180a3319e7d7063c027b22b6b8389f83c9aba5a25fcb0cee256c1c16760db9bdec0c82d90800249d026ad7e4fa1f8635c4ee66ac2ecc8ac01ec76d44981128e4000cd5a529b4011b6145ece1a1d5404a453d8f28fb6ecedf4584fc8a952cb6f9d6df93cafcd21eac2a8b63973d1643a07daf8ac82dd5b054c0d25445e09978693ac63ee2de6516fc2b9c0d2c59aa561b6029edfe22e85ec79edb34bdc7617ba11cf3d4c0f02b13c2eeb3c0faa2fd0592f8303ed4bac9e9f81e7c627493f4a334c539b8acd96f02bc66d56f149f0139979ef02b7f584ae703925e664182e410729af2b9675cb406169d27e032bfcc6e540bd4b7b1b11054f83ac31d5c5013e0ac3f6d6e6a97ec1b08b22c0d8fcec02e23c3d2f56227fdf155a9a7c363854deaecc5321bc94fc52e50df9ac72c04b4fb74cbf7cd86c427f9049ea0123dbabed674a4e2de9652290ddbf03a1835d5a92ae4ef3237137432ccc8146c33fbd50155eb4e68cabd0208196843c201f2ed0ff53d389b7845fe62eb1e873c6f5c9c1216ad8ffdfeadfb1718e74c50fe500225ee24b08374d2a8b7fc17d324f50220961759194b39f844b12739e80621a5960316675859bed49ede417bf5a5acc2e4b364b70bb92683986c1df944a9a3b8cc87e3951e156af6abca648b28be2b74e6df59df0801edc236b129ba40af8c1c9f93b26fd9a45d102fa7bf90e7f51f14dd59d241c4327642134210376431209d2593785a6a4a6f042505387ab87daf614bbafce0a71426a7e26d2ebe8f7a36e23272d68e4396afe0eb899fe01103e5190ad987834ca9971bed9c1030fa67d27207d4a97e2583383f5904f0a0ef868a6b8a0a2c85fe1c9554e620130e6c08293a578b5a28ea7c0021ceb00c5e4e171a08f2097216d3dced274a6407868f61297f9fe9e95cfa52375e1390c2a034d849bbce454638828ee58aec0b0e171dadb3c2ba34e649b6df427a1a22ec81e2406de5d9d3db670db9198fc1070ef752419ede5f9973c2105bfba9ccb21d5e02ea1e13024a8072d8d2816018970bb8e226425c4c82771c7400941b55dac1a82c6ee13100d8bda00bab5a6d4e0111f101af580b036e177b7e88d70ebf1f57d3d9869f488de7ef2e41923a71cda5153c0f99aa741088d700453e078bda0ac7cf72c4d40e5755f51e9f972d5193161ffbef1f56c0eaf0507bfe67e000aa14c77e2fc597669522fd7553bc3a340e622190378956119d6213836b644a25648d9b3463ff256fa07f431fb1254f38a2540d7d8d1ba4d4000d5470105399683fa97f32adc17549946b7d4e33a06085ea451bb9b0d600fe3202fa84f96bbe9895918438d9d23440f5d086cb4624eefc7a2c09554cefe010fc8412f7dea624ecba0b778302ee742b84e396dc43d3c76eb2b10567ee21da53b51452ef279344c3f4cf1797576436ef727f496641913bc0f28b2dd03f71184034d502788de0d7d8caa15ab98c454d2676e297017823c35c061419bd9a63ba9deda91b4a7f5cc27883af1ce981a40998cb00af959ef782a4971b2bdfd91e38d7045ff5b399c5c097e014ee9959edff45d11affda024527b1c03417b6609dd14487de0f2ab328f2c7066e2fe3d18fdad528fa097746a24139bfcd1361368052ae8ebd22969612d971a162c5cd161da46aefa32d4b540937b80ee404eae6eb13a39ce56f8fff92b54da51403fdfcf7946dd8a43ce26cdccee842af2f6eb15827f305af101464d910fb943488e0e5ba33853c6206eddceb9e2575bf15ab59f5b519e3023a368f7c1584b1f9e4e185f05cdf6b4ae976f075d4db94882711c2ab8e50b028f8f181b95604247e18cefb32c187176e0c4264b9d9e78f7c053277dd97cae1eb03dcf68ccb9c14d6f77256486d60a6ef3c8dfc54254ae5d317284ed44a8c2ef6ddf4aec97a529d841a43fdf3bff0d07776383574af343a6b2504328a61ae7607b1e792ad94fd63e0f5f7a714febd8866ecf4e93be767a16f2c6a4302ed3c88f3e59bf92c5343617ad9051655cd702ff03e0a52af7f0759fa18826dd0d6c0d6b9b58a943c485c911cc2e83d683725ff800e4f44dea9e5747c064f56dec7c38e976a8667949d972a1e8d5cf265e4f07d86e2bd4c2706fea5e50780f6bbcf004fad574131b2a5d060402b25a79ba39adebc44adec286e2641d02c06eb8264faccb1c5ba4cfe130df3ea472e3ad9df249f14eb4db5f2a7b1bc271d67455ab068c58b17a0eb2445f60d9788b899e3779594819895fadc61cc2e080daff29cee7c04c152928572b235c790f229121bf087c155efff507d086f273f11769248554252c8d5972cc79eb93724ca839565986fd0d23149a7d428b2eb18206374aaf52de2629a8215536912ce04303d8308cbe65b8b76a963b097519a531615325c2fd7a1cdd84daeaea5f0951ba4421342db3b0225bd35b659e31cff10180bd6dc65a34a851c7c9d943c9a600c76aa179626a52d8e9f77fb03b5f03100037dc56ffa38081b2a6db99e9299075f3b2c4cf6039d9586cbf123bb759bdb130475385704108ec22012b4742792a05037da6aaa56e84da60c467ea7d3a908e81d7ecf0f95150be7f4d5dec9a1dd9dc832495542d1c376395c5e7aa334e020a21b28b9245119102fd119e81c340311a9ae3749ba3384ba0f1204f6293e3937dc0eeb2fd7a2ac724da6224dfd2dbec16d3a3af4c70ef9ffc653a52eab8d1693a61bf5633124ef3a8eec373e13373abc59d5993e616593eba7dbb5e248ff802881102c6eb060d5d2b8968e9eef372763ca5183b873e5a581e9e865f47b6028bff71c19cd3c08fd68a23f0d9c99aab607387b9c6ec678f9455616311768fa832a5204da68b4a1d18935ff4523e6cf72b1850132058c7f150c2e126196b6723f19ad2fb8db2bfb259f034348221b3d3f87b85780619a6d54e67cf0a55965862b7fe52e23144adc595bc2be1a48f1c1cc41b41dd9da0dbe5adef141730e340d04585616328ade11c06b5003e195f1e3dab9003fa8013ffd3f7e6a67c1f51ebf65dad418a861e2f11f2ffe125ce67efb0eb74f33bd131b5a5d11b6446a8b3fa207ed8b2285a3475847e33e625ed4e7d6533778b595aff6607586bb0412d8210fe340552e3583fceba101476b4fd039726118ac87f80df3cba443836dc2a53fd6fcd34e0e18d5690c79a9e9db8026bb60f070b81acd43c1e5f7030ea7dd2d61bff42ea430279ed1125758d018a344fb17c2592a3d2c34594303a65937825b43c263ceff01c029ef10cc78125dd9f4a55a19c460e1ad391a367f976c8d14a2fb91320efe28825a32c3b76a082a9e42428bac2e25c4892681860db150836148b3b1fdb13d1f64fd78bb318ce7387682b34c821bedafa127f2556a9c2687178ebe195756f609fe81400fb471b4fb1d59f727b378ed82d87dcd5d496097eb293d973564d398058ff3208fb5c9cd219328dff54018be2374f4a66fa489fcdff636810ec5288a0f1428e907d19a12444d9075431e049e594078e1d039e1260075278fb374a93e0105ee257808016fb5c76dee7c048012653bed21b536bffc786e8a1d77a2b8290b41964caad2025868bca96f50308b0e8d7a814ec83b900c8f77583e86bae18a18d2d335215664076fbe4a32999c11aad848aaa1df092c39ba98380a3439943e1ee02ed6a5098b95951eb7e0447b7df1eef98bc02b65dfe974bb92a4ffccc41c2983166df1755e1dfbd5545f0d62e8a27daaead2c734a6e61a196f7c253b967c1528ef00362014c7cae847514cfffb599397f6fe6c6ee72025414d2908b47de40a84cb4e3c3424ba0329707761ff4b3262835631ea1a3e164fedf5077ad2ad20039921b628ef95d8480eb14371130e00af763739537e2d02d6f125589f25eb850ec1140ed345fc070944b3846169aaf337e145e157dba7bb5cc8ae1919e050e01864dcdeb4f64782d440a6e643bd1f58b00fa425237d8a1f8b9a014d7198127601a587ed01e73a590f2bb886b1526b9aa2f577b51ab869e9e11fb795efaaa6c31762c2ab563613d28bfb2c3d59f769eb526bff8eb35ce7a97fb655447093663f1d1095d270f54f28866027ea95f63a6e5715618c99e7e08f17948a1e8c65d0f223eb8346f3a9ca6c43ab9669df0c286376cb610b5a073c5371c2c168935bc1a82162a0e476196e2da58d90892d41a9a9ba1d51aea5d2e8fe1d3554036501ad561776fe8203c0febc1bc49c023b4f2095f7ab99dc9fa4a09c70050ea6c1a6f93d04e66dace55265374deb889c25cf2a56f3cd964ae8d3ac45ee95781743bf701a13b4cb3c6948375a09050916d900fb3d0811a077bf61fad07727b53c2564dfe216679c8ff255da315e1e475b866a908dbbbd515d620c7182b849e8863869ddd808902283f89ce762a42304280edf68f9a76b67fcb61a7a54cda8575384ef380b24d73b1e75b50a6174ae6ab664e0a75f91d4acd60402170a2bf967c9862f0ae40ea80f4d02e46dfc9d22f4c5d3358a3a741b39930b017558af7a8c9e268f37a00cd48b6a5afe6dc0785efacd85eacbf38ff3691f503bd723796d7a2b7a5505471c0a5dfa4b6eec5f56d4b7e4805da697d7a7e2878d717e21d742f9699ac0aa802f8b8c774afd950836e8ec22a520412abd775c4619aafa9dd53a9dba31f385d121f23710baecf82aadad99712d54fbbff07e19d618663216ebf13dd355389ed70ab297bb2e8f14c44d9db5aed00f4272956f940c814e02c329cac3b33e110f7b260c8b643c9225c725f638d415096330c953d46a6e6901548de47f050cea00921c406ed69b8affa8c49e5126b09b7407f1fe39e6c3a67ff985f44b4068d17ca2211a8e6004ac2ce57b738ea79b47a79212fe05f59fc333dfea5b973e0189f8711d2ac25b7da815377b5e0b79bb57edfeb0b3e249284b093f937eb3c8cd5dfc470e799e62210ee5038ca82113eb298d0a91ed7265809b0e3c41842327227bca7e179281876a2d730f88bfcde6a261d12b63b325fc4b8f8a07deaab29e073e24612e721bd4dfbc7a76064f51b97cb8c9aadaebc2219602c4018e412558f0c92cf50f0300b41c3af93217af662fae6c20aba5c5de25678a70e7548eed1465315e04260f3dcd91a642d67ec3157acf46ef336e01231612fd33a33d547b352838ff602202827e24bf1d82e874c4b2e56d6da2211d4f7ca4affadd0e17c9939bae8c861988b22b4b7bb9ef9dffa40b919b3a3182f30d07eb113eb168bd5c30bd8b206a032495cd76b9b5ded766b724b3e8a955b114b6bc8eb936462efea06162c87c2c1caaf7adc04449a6a314f788d3f2e7ad03a89388119434eb73467f6a22898b762192c947c632f83b1d839ac813849f7f22884a21461081a6d636bc2c044241732a226e71541009557abe207a50bb07ee6e5318c4ee2f10bb0bcda8e4fe00e6b71fc41469002dd8f72c043824f87eeaab33b31ac8effef573e9f15bb24f1af840107e0034eaaa8cce6143056e561c708175ebd2bd2b01f7204024529b36328ec107e1079b23231ca0576310db1dcddae6dfb2272b968d23ce655bfaa3b5b578bb051aa676102be135cff656a57bf369ca177e279e14f723c6d567cfa0cce54170301665691ed411acc68341b6e22d99e80c111504fb6e8fa565540666a6c139f21693e4c8d39fee103747a085926e9c36d672e0950b53e0168b556f7cb1a672ec12a0aa0eab6371061b8532a895927a22ff8cd0d1b0f4c4daa831cfdb62551d422abedad3879ba0e5d639f4ea58196f23087af2ffd496a7572303590001f57c652e0d004ca35307c2f8c99a335d6378a3770e5dea4aeea8e5a4493369f406fbdd1bb85993f3834486eb72a18278e27c76aa92986a4b9285143493f120104d628016e1f540e94796eb5b0014ee099328f84ceb614add86ccfbc550f7600f3413651c6d6c29222e50e8a15edef04969562fdbee5ffa843960d92d565d380cbe1def21d8212fb0ef618b09a78a0d3030ea274197521f239b7b82874d575222ec0ba81ee82dca840e340012136ef0b243eba0e650058aac4a5fc88ad5327ad848ecc710d71beeb1dfa88fb932d6125e181271335a9083d7fc6e13cdb92d1ea5b3d60912591a5d8eab8b8db19ca4336fc32c3147ffe8088684fdcaebf5f634cf5e1eab1b68a4d96dbdaa0afd40748e0b4a0ebfba0a627f30c9dbc621e727f7bef5e78e245f34dcd0a1828753e5723eaa14bb4313dbe15d5e76965f8bb765910f417ae403a931a5b653d4fbe31332fd04fc6e69626fb834cda6fe48674b98219ec25e212df043b86786dd8b06d34b0bb7e6cf4c5bbaa01158fc330b05fcee8b4fb9eed20ead836e6b8cdbc6cbc06457ac64cf3a72283181e35a9657ba381ed47169ef3f178a2f40907c36a2de63a99c8cbe92a23d9b10305c3003f5341ef1f9bdef0a99000586c10ea5861a049a13f7c69b0bf6526387aedb3952554218cb5ae0266b3d17107b45cca47a5078acf00a36247bd987db1d331c585953c27b3e7b68e34c5c086f1e38aef9ef67f6bd98ba574b8534c2f71e4ec038565f0b24c6faeeff168926ae5209a04acf180a8c7481af21deeb7a02886f61625e734604feae11bb965306c08ab20127db181eb638f57097f85600f251529faea4299afb46dc539ed447212bbf694211232606e3fc74627321d1188ba6d02f9e8269dd81843182e41575113b3b7b2c3b1a8698546d3aec50985c37a3184edcc69efc421fd3e4f939fde4182255831a27d9b5d835ed40def306d138225b855aa0b81e5707438d4cf7408725dd70cb357f50ec938767a6216c3c45ef2034d3fbc3c8555a67b7dc78b47d8628fd188cef3d2b8dfbf4fc38ef79f480b2efd8c4adfd46fb6205e251212ebaf31e70b2d8a82ef799db3afddc46ae635734ac0c700fe84891c053e63156981b0b0fdba632fd2f3c239ee4bd04254130f5d66e68515143c5f4d6f2b29e17bcb1a303aa93928c09c5312a4d16946121da973879c2af82cf71b14a2ecbeda0e5ec8b02462777d72e300a2f695fe1e4440becc12fea0e2ca14ff1862931e8175d31cb1c9f8844549a6ea22385d3a4f60cd14fd6425aec8890bde9221d42f60e54bca41eea53a6a00c6fe7b66f129dec025d3c05b1a7661322b803efc62b123918b9700799cf50fecd11f0787f2807bf01fbec1f9774eb693afa70d652dd59d292b74809f0b45fd23a36228017f8cfaffd39368d2317dfa86d5eeefabb69e13c7d7ca5128829e70548ddef6f0d1230f3674f752890c754e20a858ff699eb425595132818423036c330b0a4c86f547594d8e34b5abacc9933308d03ff4b5307c9f44b721d9e1d6655d4140a8adca0117a6ac07565e7e8887975eb3965583949cd93c18900bb301068bcbc64aa85a14a8ef8365ea3f57bbe465f80ed8c039a04a7c9e5da0e09ec9c44885c8ab72021e4f8f6c388240c577957dbdd7f64e929aa54c2960a071e8b4cd84f4f659b95605e75c42ca42547f2462b36f75b57972e6f9859d45f067723d028b166e84ad2a5d9921bf762376c3e90ae6b17bd87c27e0fc007a77d1f5f7d8b0028999ef82f084a250d8562793aa0ed27644ad82feb94e80d948a6f09feb51fd95f0632eb2b356c130b50c4f70fb0f967cb7eeddb222c546a5efb49050476a0d41bb023dc5d85b1cd7776f9a2c3c792be49704ef8e0bb09b9e6913728a0b8490ce3a1277aaa62aab1f07ebb41c1f395c34eb9e983884e8dd953bf4a0f0e353000cb746ce229ae769ee10c47f15ec40591d589d798d0c2bd2655751106774bd58d59720491a086b4721fe745209895969578d037e71ae982f8a9320322a859297e7a85a2002ebe2e883aaad92af39668cd0798c08b7eddd7d503b24b1ae2daa5c4eb25393ee6ebf4008bc4e0c072ca90b67a65c586fcd5550b29423f093d0da983505afaafb1eb0771e8e9e1cb3539fbe61f5efca2d4860b1fa1b36121e5effbfaf7611b946208ea448d990b3cfdaf50b40063af0b7cccab49f5c86b16506192e554f89afa326ed0528f6aa248eba2da1825d58628355393113a8fee286a02b1c4a452f8f15011bf27dfa4fd6832f09725a7219294e11954b957f9221166c24763a83391287fc3eefe14933c5c14f4c7d6937f7b75542347d04045a110890ee0f78b861b3af29a624e25b367017d565f468cddce5b7f0cf3c35939a710bf47754178cfcde50bc00ad8afdeb5f65ac80bf3130f8f916859fdd0d676b51a19030af15d64ddc9aefd991cd5cfa036cb80826b4373b9d7bcc892d88b7dc91054be1686426a50d43560b6f5a738e11ac395dab9fe8c808f8f8ceef391350406b72a33744c6e8340275bc6ba889bb2a39e4e44a5ec3608ba7e9ff3169958a41fdf27650f39023fd280fbcd8a14f01e01129e55d1f39690748e99810326fdcd20e052a932671c905b1eaa5c122a28ec8ee55385d4230903c923a10347dfdee01e17b2763b6a7060a5b8096bdf84673c406e1bbeb5fba30c109497c0aa3ce4d91814c363d790b240b21d0c46bca0a903c5fee1e5fd6e2278a36d7209bff11e701244171b2ba258a8c4b611fe9d842c33b90dc2bb751a4c9834b94ee633ff2f0c17e99d90e05e03e67dcc81f0482fe23b3edcf8af33abbd48817fcd2f01e5cbad02174e2613f854965fff32fcfe1b2ceb878c5e8451dd0aa5d75a96db1423038d224a2a1f5665b42ccf0cd2009d22d026f92aed1329fa0eeeac0bd789e8a094c82617ebca343afad708cbf7746a1a7b486be884219e41fc54bc92afa7c4fde27204e3b778f1434022e9a0f5af3cb796f94594c41fe3d1343ce8e95cfa3405e76024841490ba15b8d6e571c78b5fa9bdb4f42cf6939f0ca766f8a33f9af1388e9f0cb4e92a32b3f70b729d395a955e2080b6f0ef0d26e8692148295a7b56b5f6a31596ef09d919d4e9ff98704206e61a8dc0a9ce4662eaf6a10878f3180ecd6bcb0093e2dacd06fe78b044dd4e2f973281e38c793a05bc453015dd5d99c1adfa70234a32f957d93338fde57563ebaf78674ac0ecfd3bfe0a8293a8222791613d2c2ffeacc050d243fae78fecd4172ef8ac8d65605f37137ac2d95706f7fd8f331204bbed683e9949fba2e4bf9941b4939ee707c3973843138bebfee49358309dd60265751d08c2e979f56c6bd140921e7a66e34962c2e608d7326c40f95b797636285580e22fa6e96ffbdfa09b8be7d9c5def6bb69b24b832097fc8501a8ca707c0b7f05f0505eb62b7964b96d0c4913735b728fa4baf217082e8f47e2f582872412c7a05dcc806ce1d8cddcca1d989321c22e660aeba3d8945d7f932b6de9e6591b97a10e4e7f2ac05c0135756d3e162058d1b9e577760bfa12f779dc1b21c04300b8aa9e06aa51ffdcd11ff50c67a307276719bc15611fd86c8098faf7c829da2c4b1f4eb53f6fb595e1961d38512f3ce9827b9e4822b90fe6e5c0c52672cc1d012e591b62f714b671321c5ce756f826cdc2bd630a9a79272f7aa02baf56779d25d42ac69b671532b1d51d23e7c79160bc322015ca838b4e674eea5167aba83c218ea51067f7176b027556fd11238b4bcf8865d89a9cd0bb3e4f43477398ccf22aa043b2ff7867dea2472de15e42e20bfccd0fd02248e6f1ebf7f95edc2eb2312c0a6b7ad4f50c3bf03555011fdf65cd0e2fa85d3bb7df6dd924e7bb5794985118a1d10561c8f03828a912b04d1e50f8bde69235134a4d4ae2da48c118a5dce12e703a9115055cdecba78a75a6d10a5e56613e9dc3efd226cd0756e592f880642c9974ff67e8a91633f52d02e73ef44399b35cab4796c37156f0f9800b7a88660345dcbf63e44f1fa2d1f8eab547a67f70db529978f40217b5422eed2588a4ee300457f11907f9415e349dad8cb86d9910eebe588cc8c0cc798a522e39a669031bcc7172286c9537143a92c965481a89f3c4e860d5ca9824175d9c7a597ee7c61c115ec99fde4909f191e31613746673c5507ab6e01436e6fe5f07d8a929fef51ff7676cbb3def22e44a545acbb9aaee1773f706fc8684a7b465d2f92f8af7dc0a1800216db2c08004b42287c6df656f6b5dedaae8e1b9013c94532463df05952bbf330909de4d033727d32480cae73d2a2e9e50326fc605ae30c0c9f011128b1a678d3619cafef8cf5d5e07d61a849c05e91e9ce8290b8e0ee69c705736ed81117bbbb0fad9bfc93fbeda8ec8b9464853ee161b5fa9b5e954c36d10f7d2e01d0837f15100dc35eb17b2dad7ccd87b482048827c1716d510244380ac5b0934e3124b0552ca7a5902213329c982bf38782b0aa337a618d658aec9d01e4885977f1514eb3c815c8ac482050a2d19638a584180f196c16339e4652b1b7f1ad870891d63a0420da9390f9dc04588fdfb066906fbeda08ee28cf7f06541519f991aa7125c2099036a614739e41d2ee4266b3faec273304a8a7fb09758a64cdae16d281d06427553cb1acf30f1603889e575889f826a832acc44180f68c230647d971413a392f060a527885509f15ad448f8aece5aadc9836862138a8b6d3b52c33dd41faa36de1e5f4355825cc13a32d55f9d84a7337b691d12ab95d7eddcdd3f72a22ee84a7b8932c0aec98b47176220beadc273414c3a7d739e598b932603544fab03f191cf63a65ae49a832011f34c94d75bb99361b17399b27d77231128adfb33018fd3f19e9f95a38b4f4296e1b8ac28f6ee41814be442b889ffd99b621ce5bf1bf69588a55456a7f9911222a4825f68d22106aaf2ee07891bbcae65d952e75b2ce90f558af3f890bff0115d005d1e5f714935cb35aa8e12c3b0f194f53ba20427e5d13527c1a9f7563f61ae1f59a6dc16f2cca1df47ba4d47ad490a54d1a9af10e1bfddf43ad707f810b5b9b48a91b16d892c73403c3f5cd6670bf79e355e1d1cb721ea112226ce6a6d52283c887da649f153a7790ec04f440176987419b5421b7c694c646cbc5618e0f2e9d9ca5a10be1c9e007d519aebc3fcaa904cf4028d0f15c92504ec8655c6f5ec0bdac134b4194ab85c505cca657e295d2041c68be114b83a369cc7707cd41264238945dcc4d2581939579ebdc673ae8a5ec925776e28341d66f9f6eae02473c10b21c2f682ba22fd78f1b25895ac2adad8511422110bdfcc50e8b7e10c22d1061ca72d0945d2ae76eb874cca31ae41ce92e335e6732e0ecb8257b64e85774c95c601b69ccdd6cbdb5bce2bdb755e0f4309e735725f", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" - }, - "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000dfa79688268e94f45000000000000000000000000000000000000000000000003b27d8c4969fe006f000000000000000000000000000000000000000000000008596197ae31284d9a00000000000000000000000000000000000000000000000000025071b1b78e460000000000000000000000000000000000000000000000049f29ba73988453810000000000000000000000000000000000000000000000037166cb36624c1ddc000000000000000000000000000000000000000000000007d050eee5ee699fe8000000000000000000000000000000000000000000000000000258bb7095c7a3000000000000000000000000000000000000000000000005704f0d79a04cd41f000000000000000000000000000000000000000000000001cc8608e7634392ef00000000000000000000000000000000000000000000000af88bb11f4b13a3c200000000000000000000000000000000000000000000000000029db74f7fe7cd00000000000000000000000000000000000000000000000f4a5900047ae9ba6400000000000000000000000000000000000000000000000a2f7b686593ac5c9b000000000000000000000000000000000000000000000006e5e184d039f43481000000000000000000000000000000000000000000000000000265f972d900ce28738b09735682ead7a4c64799135201b3899d799236623c3b56cde6eeffc14c0df0717f0a39df3d76c516e8bf543821b9f4ba63b14188d420b92f5ed32350042061c4c31ac25d95dc3913a7a4720dcbd0d2b502eea4245f0e8e0b648eb3664f0ffcf0587456eae3287e653a00e8edaaebecd3e65d8ec98782517ef25ca5f5ed17e994084f3bba80177c052107cd4673933d4d7d498712c3c1a765d4ddd106cf23121b833a7165f5c9a7cbae738a5033ca28022a37b8399326468eccd785a1650e921736cbaea5f086a1fc0b8d34c9b3cf259d4260e3a0fb734e9ba562371b5d22be915dc76a01693899c6a34254a2ee1d2034d4b4afb734de2afae62fe3949727c3b05c1e3ca04b6abb43df9172b580ce32a1ea52c6a13bdabefe41b291b61b0acf905985d7fe37c2a12230fc1fe3c698d1495d937e0315f8ebc4f08b81f07b054fd658b021f59f1486e8105180cf9ea58448782cea621e76ef6e2b502df53c097f54506c667839a3fd6816ce0201339146d1334deb91f8ea6a14405ad6153a182d352d88c84c0f0d3446535350ae13ab7965e65605009887d7909a2fa1d42e2896551a0829706edd47b1b5786e28188506120cab5c15268750c76c2282e001032b6979a88d90b20a1db9d030107ffc0677ebcb6777c7f4ca2ee0510182362e2657252207babe037b83a8d8b3dc52cc67e771540a593d889433e9cb38eec2d71d52810b29a729959f506e4ee054bd6382f9acfcfa7c288777d3a5f4797e9342229726d66b20423a7f33a255cc294690bea014d9afb0331f91401058a271c34d2ab341191877abb7207f8754970389d7dfcfa00ec4ffaaeb0524dd716cfe7cb11e9223897133507d5227ce3ce11671f837ab1ef85d0aa6e308de0ecae5e49f712bf468ea81ea81e5527ee9e0f1a6128b1d1d32c56b82e3da07269e74c46c9978078a4fc2c3ec815158339b79250151302bb388375cdeada8e2669ddf041f19621eabb305f4d3270b826fa6ed2f25db9ffe39fc47e81839abfbecd91abc210133053d60869e919a115fda8d5046e59bfb2f874261d17b698de9e524a7145be6c521e3e2c29389e805e67940c921c0dd97b619b10e3d3e1822e3b2906d448ec85a031c38b5d1f34c2d4b5b7d5b6360c16fc3f097a28fc83c272b1fce5a6398bbe20a3997e59fc3a0e2317de98fdb917166780523993068ec1cdcd514df1fe9083f1991ee90b63c34c18b2639cc3c3df351ba4b8ee53f5663219c845085116bb81c1639cac4dda73a16cc4d7f9e8e607200c85a8e0fad44becf3891ffe93aa770132a39310b85cbbff14f0bce323cc23f445f6a258707355b3779232a642eff4b1a09e4b99928bfd02b699bf248fd01e881d98aef5772a6043cd5619e1a61af8b3303c7e35dd434b2854019587951ffb7e885f616ef360789979c7851ff5bb479421f42bb812a236b6fca6f7ded6823a28d36810b5eac6732fc2ad4410f715951cb2b5e09eda26e7793afd5c7c865de1570c4e72d9a88ce4b31a88264717a070d802a817a31b2c84d4c1f13a918c3a73c88d8a67f6c81fcf41ecd278894e88485c5062a3ef24ec877338c2cbafd1e61205fa95e2b93c59326cbf8f6468453a1e85e054db79eff791b9a1cb823dd9566f06129dce2b1f1c15e9c6d878629d40f61512eced624da055ff54fd71cf3520e5a759b33d31ce3d711c6e1163e3645a725960c4b1b6a5254cb9b135176e02075b45e9e1ea54e21b1a420b1414abc132d02be133cc2dfd9cdbf2a0a44fd11b51779483b4052c658b345b31ba93b918522167102cd4824015fe51e5b84a6fb335ac4842f9ed11d6f5e69b4d8dd8ba64e0f36270f78b42c71621f1501e1ba49fa4e661de16cb8dd3c698093969b8964809106412790264f0ae9b8007b76649a949b69ac1d6ec2a955902ececb7d33336b485a7c2c3c6a1fadf0fd121f79fface6cdf947dae0f776d069829fa900f514ece4141616bbbedf7de53e30cdfab6d5619028b321d44e93184fdf01e7ab3f5a4424386c23b8e5447413e6639c215e2f7bacbbe08458fe90fcff82f4dc73b1b9fa944e91108a5c28ea9df2a9e1b17874a186d1d5301848073af15ed52a4836a71b9576fb284357773419bdaa332b8130670664dea49b96012f02b091e273421d579b8b7c0db2b2715658dfaf910c32aab84a6eb1daa3f8c4c72cd2b43abc44cdb4aa8a0b0dc67c649a6c3b9cdcbea178401c0abcb272d8d5240a492956cdb2e4a3de78841ec42a7c9a5ab3b10d4264cf887b1c81451ebfb5e4277942e5736a7049bc071c168f11d6111ca6731a245c4f6f0aa9acba6d334d552193f8453f06e454c0f1b30e3264e919b2c667507684454b0045f26da17f27caf045c7b4d2f6be92bfa160277b4cf55287f67b9560a68e53fde3b7b85ee9043123cd7039658e7f874c136d1a2c26f2deaf00a930cd611ae4ca28ec30d65792e9493c2175e557b21e31a5102a8f42f94c87e31eb528d7a5fd4ca83ac3a1b62ed65113bf25e5e0a82dd2cae52cba89c170eaf2134fd4b818630ab330f68c040bca7285e619b2d493d713fbfe079f72b0b80fe7f56ea6eb5b20bf7990fa72a7f26191f159a2eb29d01801330d26bad38bdbd921c0e68d44a56c432329d5477f1e52f1d720e9dfb09f24fcc2f610e4303caced66f68ebcb8d876ec6f7c1904ab11800505a020eb41124069deb92d748417971506e406a675a65eebf63796897742b6080c037c097e742a446ed528783529668433efa5bade2e3f9052c11418f54b8bac5ae047f5fd30ba9e031f2bf38e36125c725b69365847ab2eaffb80c917ac7d3a97a47c1a1a9950db209d0326fe9bb980be5ef2bf21a3e85f236f53aa3843581e6e49d3b4451db82ff50218cdf7411b81f746a1086be6f27cffbf9f2352b4fca25777ae421cb673afe74d276522587e772895b53893a247b5bfb427dacfe13c2992700becb2313d3dc00f1876d430708da7298c907dff0b5984eafba5b26cd6fdfd59363b0bc511769da02db19b422e86e8e4003ad6554001fd10fd87d5f32af8a9d13f23bbe12525038a1a7a3867ea9741941c29e3aaa8491855617b16f140ded2b19a0b03cc4a4cd74c20f4111cdff9aaca53255e23c1c6412be63bccc4930ef87a63fb8e59411f56d4214b5fdb1a07fb3d5e91b25b3df5df862ad9cd72d3a24325c122801358aa149d2c03a7e24a687a2135762b2c1108cf1432e73f5ebb8e20a552ad92a91dbb185f11f9ede94d1ecebb9b30bc4c1a7744e2d121a63bb8fb54c2a83dcb8bb61cd6b600571edfb0c5ae31403c4fdebd9bfba727fe0422077b4bc1b660ad5ec5db84b7279d5845420a92bc08b2e5698b81383cc1490904ea5f20406d04b670ac20b49919e8beffaf81535d507d4ca8b08778bb8b791d016fb0c935e0ad7f1c6146cc4a262ce42077ad7d82e2e3848618c2e9514a3422581329e323cfb5e6153a6131990a4326ca63611c14ab69058cdfe7289463baabde69bfb64c895637ad70f47acb101d099896c7ba7b7aaeb2292c376efdfe9278aeabc23363ec0d2c0fad2cf14325801e832e9d2edeffbe63a053f8d6ac11a13118adbe3cb26f66bdc367bdf2a11bf5271e7b3828c52104b02b5f157695823763e7093e3e9e680921cd0c3dce450f4251845969a5c04cfa21f68c982ad2303130bdb22134d6210746c76817e25a249c64fff8b9d2e3651db73d13eea4360f99ebea5b17fd312fee8b1117e4967004fcf53c8939497fd46ee37dc67a58480fa1d8f2cba62cd1a04755796bdb1dbd0625d390458c05bc57aa6cec0ecf86e5b90d7d7484ce043ba332d42367e4b5310c45f065d59ed77cdf230d709944f6dab4c7782c82a2ce64161caa970c2c071c11e59f3300de1c8e44688c10c9214d8ee5d1402f31710c6c033adcef7c521b550cfbf8b66cb58eaf5b4f600593e7a7337c5b37df382e5df9bb7da69acd0935db2f71443d93c858a05143b20f06298edcc053d6f52755f8791f02c80856e1f55b0eab451543b5001697d60235605a12ca6377956f5161eabb574ea08f2741d0501dc63400f0e57099a68241c1d29056336dd70b0ed371da2615573480f10f933212ec6e0b1f25cbfe5ab5958a232a3c6555bc5d9a88ca2a5433eeb71500a178dc1003e570f070517a79e4ad9d72892bf563aebc21f65a15617fba60ab62749c622bb9822e4621ceeae8fc692ec1384c31b3fa7f85ea545106bbd4f0c8b5b03a6a0f64ca4fc38aaf821910592887b951fe39a88fed773f3c32e9d3afc97ee079d406d44e6772130b54178d8e84cf33390e8b880c554746bf355472185cbff9ba4c218facbf53bb5db501c6c583ac7df90901c1a4c7bfad4dc5a1f811986294c3e30b018f42f8d486433a79cebc05ab5a4861a0a19f413346be6108d93bbb7ab24d0f1e374662c55fbd4000b52009915ae41ace2fd2798513822da5c47b69c627522fa60c93b96255feeb8fead0646f6c03c55bdf0ce8c90d8a4ced28275e6005101dff152a15dedc7a5077b8a6a24fb6ba47511dcf942b097d341439041c53dd82030dff114cb5c1acff5f422b9f9157306540025097b5ca9666848657a1bdd8ac035532d8747b5182f12d2d76caa80b6ef5f2c23ed91c230c03f3c38b667fe41f03c5292df7fcc0485405a81a40666c864a1c0bd0f9797d7f00211a2a4d7073be1a8cc3df21420e1ac68e87a2eb75b32473cae6554a8c5118662b250f3c8e00ae2e2973ca4bf0d376bfeeaabf8f7b5c4eb4761a0b51fc018d7a205f02979d60c2027172019511cbaf761d504bfb9dfc8e01e45cd38b4e10103c13b2146174d8fb073f021f5d8fb8369b690f7a5df4044a2acc9e308e6165e6f7f5ea5d0d5f695a201986328ac16e02384a0f49de5929baa1956458ee19a901e8fbe31ecfa803bd2954fe80592a60132ae23e7c8d4d79dca4d60db2dbfe343b01cbab95a814deef0d2f09686caba0a5e8714860f809bff73ea57ed27ff26c921247633ff0f53d010898444c0522085bf95583c6bc1ea11245794f6442c79c3467afd6de0e938b9905fee8793edd702dfee158d66580b78e7124a0aea44968f9ded2dd0500d8263218735ffa49cc44890afd2777727aa4f9895a7039122d6e333b3520dde3f6096c29ec16859e98a504c88d771cc47aec7ff40a9e4f201ca83dfc7574e01261a9ea214114b3db1469a1cf55002bbd38d0ea429b1e24004d7677a57f16b4b0cbc25803108c1d04f5c0c3ef669689f62188b71909f1c11d95d514ca3a78edc8a7976422a8c196a42444ac4076ff06e369f839e38244111f04d4f74cc3ca2d635bc3001c60ee36b09471a5ed2d5db3a067183c50ddd40f2d74469507e949541d101d7e16c41645a3fc37a00b4708cb588611fe15d807d9861ea110e6883f4df31cc8ec278d447019f5d65f772020851c65d6a10ea601c7c4bcf78e58fc905b2c664f0d2c457a65f6424d6739ccaefc361f1e700d1735c61328bc29e75cb0d76b1c013910c82a1c8b34214adff97e9d94943f069509a46742bcd835da11ebb9b440dd3624c70366a5708a3a10b9688401e4250bf48266aa77d0ec6e5403d5db41f718f30f947098f457716160288bdb4721cea1adc69d2745d7aefc57eafbd2e901013b2ccf7ff182845d214e0fbe248a08364934d0ff522a9fd3005df960972ab255052930025839cab3bfbb81d5127ddf6fa01a61a613a6e8f128a85004743292f3cd2c1792d9ba72f886ed7a121297461743136ce348913ca557297019e2a3aec0a51922836c33d87bb152b06b9b9f3bbd144c703eb94cdf020568660d841a255e5d276bde9c1bf661ff908ef89874dde149fc4dceb010e3a6a382e292716cb4b8d916263774fee4ab4ba83a3ebf0b1064ebd75b9ae887644471f86a6bf8221107cc078182f2a821d52e0cfec07bc6657e50526ef9fd0048ec552d0808a742742aa625f90291e0ca4e5a4bd0c2588f4f2072da4cbc09e48791ccc16cea22ca882cd00d0381fb3133577f3d82a0a0bdcb0db0e88fe939063377ee25026eaabd38be1902111b320f710bb3503857b61c13fed36475db6ed1080063626c619d22f9b0932aa34375d85de0439ab7c194f504bc2523abd9627a8eb0c3ddfd751df66f5c95293d54b5e457bb7a9f8362a8f0e2a8acc4586c5f9109067fce81c82242a2eb5f2558d88d1bb575b0651061ce1f9bc8c75a95c5f785ef7933948632c1cb5b43592f88a62189b6ddad9274e56b188dcf13db455a4801c175f2bf802dd46ce5aae405421fc32aa47ef04f6c1ae84584ad7f02e065e6a7cfe8d1854a0f5431c7dadb0437a9c689c31a4cea1d48dc8bc6b635382ab3f7dcab912b6169fa9550900047047ef7b1b8393d511c82c6551d90fecf2662f336597b24113124e3d13b21b27b05f81ed1c2b356defe173c7cb7e4a5d33e4dda4cf7020e7fb85bf2893696a1a3238660d480aafbb0c4271397203e1a80122314f7b9ad807960830b0cacf78e1b1d45f76d3dce3e1db3071e9055eddca41a84da428fd19241adbc656e5cf7255b0cf5ad55a164b6805f639c3f7202975845aaa64ec03b57fa721daf278099b20c04630ee5a1d2cf8aa318da7097dfc780e843e1e2b8d7a550db5cad5d2ad4cfbf2e115682cc648ca983330ffb7283318b6311873be9977637d3acc6b2eb932bd00f387b1c9aed7bf928c15e9af4b7c165e4b59cc38d8e190b8b3a81d1c6d9f83725e0695fb46317bebde0aebedd01b2ed491ce402d501aefc8f4c1c293705be6b000354686877258d5ec4bec33fe416c64ce5925bc9d9a537b439250967bf458702d23d65decbeb6752b51e297d25b2432a6f5b64c0773b205c4e1446b087a44324cce538d0ae9959129c92b2240f863c773941aa0d1c503d20b91f430a467b4f2bbe0a36f9facb42624703f09f4288e1c7b74a9cbc5b2bdaf212a49f219b618c2580560061039f9eb6c52b7688ff624844a0a4acda0f1db128eef1bd0259ac3c176b1db37e377cb9d25a85c75ba3643e894de5bd3e998a4160837cd75f1cdc723038cec04a4853a6ba6c62cd4918f1632ac30de6ed5c0f8b0a68398ee741e60f184ea704315635056068c59f3feb2e28bba86ca05e77abe071bf4d179a0e9e2d0d2bcc51f8249afca59bf04cfb916937a0183917178db300a3d477e6c83f018f0db99b774c888c7019932c91882095d48b2c84a5004642e7ab200a22e90de77515c4e35e9d6ec6d71aba421ab36b30e7328fa15dd936f1c740838181ff349e122ffeb6ee1157415c7f104ac0f38ed2cfad62acb25a58a89eea6a5f66b4bf392520de5b7a89e66f24a828b7141b01fa5c1067327831d040d0e7a07f565528f2ef250121b6f9948bb2f3f2cb4099853f11073ea5173ed10bf2cd22f90108fc7c4c231b03d6f484e3bf256fd3d4f82388d90090764a9b6eaf6cb2cc2806c5fff469294e11a6fafce22457c6817315bc429ad19853b7957af2af6c3389e10c5a105a2e8d982fe73afb4addc3aa9b344fabc343c6438c8a59098f5936add0947217cc2ab165ac1555e0264108944578a3738595d56b7e274644af28a52c992a33209c05dfa9b8864f3155f77a4b18eb1eb793f4721180a337c7fb5a0b499ea96afd5d00044ba5da625f9a36bc38107cbedf12e0a51a8a2f59f2096e0d20e065018da92bc0a7112a7e48a4444a728d936338120a122db00a096cb486e3b67164120ed11205e12b3245fff17ccc63ec28b2e2a1f3d2bc7c8e7403e0c9c5d5a1514a2b900797eb3f3ea1748dfe9e23cad65e839d8e3074d0fd24a1bec13252b57f821f9d068c1cc3c26382875bc0d27da9d3bed3fdfed96ce2b7ddd3446e35d91b31d301164e3aaaaa7534f8ad4eb17b0621e6e5c876d84fa1937a023d2c29e37bb14c9202c71cc7c96f699ecaacae99aa36e15dd8055793bc6276958cbcad551d2448d1199499f2720dc367e892cbec806b3c9459f49f22dfa2b990fc8282a4ce499ba20799ba426355a8ef8dd0fd899bcfc06372d212c6ac1f63c8bd5e6914b18cbcb61704504f2e1e2ed6d86e2da36bbac86aeca425a6720cb4facfb7fd429b85ad57032d86246b281117624b1b92e7da1c0d27bbbff6a32918fc2f5945cd0b4a0ae312ce4799fc5d07c7180005bab2956d23025adfa9e3e37b7a6166084eaec7740518fa072d7ef56b4c1f8aa21142872240e2dbebec78db08384dac3cf7c36628851830f724379349291aedfc0f9cf05c92d4af5be0a50f586929489f98bc520eb906de5f1f41ebe26c58492fcf53552fa0d1df87ab01ecf27efd93652d85bf06180c0bd83850c4ddf904a68bb0f3a24d6703fbb5e7e3e3d58561e1f21783729acc1c3d2d9b96213fba91b08d5a06eca9adb713d1636c1c20ee1c6ff40a786694870e24656dcaccb56da1ff15761c4128774105f4869db44cd54a07414e76bae9ab2bd6f76e121840d8e8f87364deaa979ff118965f2ccb1ef97b3e45187af7eee1034b6d88504c0eb3f27f35f0f36cced25475b2f62df8cceedd3f4a9c7808fe41219bca2c439d0b29739bea7622a1d7c44d8a1de6d59c63b7ab55283f5df05cb70692e71c660ed45d49f1fb0790bdf274bb6cfadb9fe7d0c436f8f784478cc8df189e594ea67b7432713bf22c1199d90e0043695858aa46cccc397e528741c6b52e19c9ff45986020241cec343c8d8f74989c21f2b86e94a704335942c967bc9c032ae6b105131dbd3e550889cd96d3577f80542ab10e53487ebad39ca428ecbb236b4ac29357495fdbcd24b194b37aef7798539cb7407fc2d16cc02647616fda1bcd71bbaf8633ca0fcd9bb267a051933c14313c8a09c665da7ab26b142e9d811d717384396cecc500f378df1db9683fda74d5bc2c4e78fa1a89f7cf4975ad69066156264ceee9b8b8926b5a7105c90889998882ac9420f532112a6f1f2f2b9c023a3ed64af572f19d017aa3ef6afaab087d3bd768155bd3df0fc9f7f822ca88009d54f9ab76967e873ce437bd0f7e3195cb48599df46f54ef9d2e424a93f59213e02a423870816fe39839111cf60a4d2ec4acd8e43863cecead8f660361b8a903bb39e0d785dcdfdbe89b85c713b3355eee749fb6027ef71e9984d38806510f18a4b230924b0b848a291f8e62922077c1034005c5ee611f550a746bba75289e04ea00ca60efb7b8587281ce5223eaaf8a07771df77a7dfee0d61b4d1e01602517cdae4f6c4ad7205b5c2d781c36bb7f9c36d3a1db2c4eec78e001dc931594700617d097a075cf9b4e688aff67aeef2b5befdca4f21e388b8c655c2b55c24e511251c2d400d40a3946224c052f1ce75a4442306569e701bcc086439a62f9e04c0758fd7bda12e1ce40b209d30d67f2f003fea544afaa1d0420f6608116269508081983a5d8a574b216c19749a1472029898399220ee4a6506f95e7c5787f5b9925e200ee2c2cf08cf40b446659aa9b72d867e89de012b1336c390f8884c2087e09f19183af33cab63051be64938e007258d7bc38158dc0e80b4f2b76d13813ba2926c0c2da99395ffbf9e8cd65c95721cffa67de1b3aacb1e40f9a75ae46d9a11e06d148dfe7adc51debf3a5ed2958ac53f09e30b368648762c53d629ea154aa10cfdd10faa8de5faa738ed3d7bded2740c0187ffd6fb2f51ae27931572d853d27b75276de25a8deb23b075f1581f71d8b45f9525f54c924a3057476123f900f1f8fc748b50883c13789486e4a0750c4c2b32d747542761d6b41b5afdb17086b17b7f3f2125e5df91bd8ff04ec54b3b732214ab81bf6e21560db4780f1f0ce7329d6540a217743efe23cd1c6694b52623718e1478973ba5f76afd37d376b8930082c5adf2d53ad44dd629106b3c3b3ef24829f1570d6fe0e6bc673a33d3f233406fbe881c13361f5495c94f2d43887657f474a5ca6ebdad16ee92ebf3755c619252bb90cee7c61ef1b3729012c54101ff03af47b6444e81ace561d759d309b050a82f6e71cfd7854a68ef7452aa7e48f7c5ae0d48b5f2d54902687a8f0bb78852bbcb6b17cc2f8cac0ec1526e2a60015d3efd9a0426d82aeee4260b71813bde80392ede8d86bf0d16af0977963ce5a573f7d561fcd20ab05e2db8ea7ccb334ab0307670beb2978093fdf7001199dd0d72b0b7752ecfb91da30847c8243072a7c2f199d811b6a6c1635c6ec835b05fd77235cb3d8451194f699293e70133d756f2c9881849ce8eae584597a83c53a47450a578d88f75f6982d17ddea04e9b9ed5095939009049cfc7cd6cf9f412a3cde99720adb91ee077da2aeafbc7a033044a0b672f1ad786fbd543528c38703fb4a37c1db9a2674feabf00ff33a5ceb01bab1aa064e846cd5d748df1714875b6a62c9dfe562519ab164540c0d7530589c288064e7b496b7184387d1d4686c18f22110364c55e53f3a70d016832be570218a012f6b252d06f73d254937d9062a293346adc20823419633ffed17822bc3259dc061a1a8863d10da15f11186f71f04b20e0d152385bc108ff740a68bf824f890f05b422bc8f53d3255aff1e028a79f6da0e0e24e931497d03ca96b04e687a6f6300ef2bf5af84353c1e1a3aec79fe45be15ff7c3791aa7dbb919da84d1106515f2d5ecbc9c4928ef292fc4de434d4b92ac996d21ebf2d7d0b9d99a295622cf2ed2ef5cbf883993dd846bae8f4fdbae671e7338c6e1743fb26ecfd0e40e409c60b03445ba5b1bad93b6d8fd88b0dfb82ea5a9a01db23cf5f74ec68b4ea42a309c624208abd552ce7d9b316865cbfad7d0c232c8f3589cc58f93168ad5fef7c3843002e357ed1e5708133f433b4e0976a4b1d8f75d99556678833e17699e399f9970d0053375c14a0ab5cd8135fe265e537ae1d98e32f64258458b7ca404128db1e2c187a91f43be9f07bbdf161b002391f1b31d31a2c8f62d7dab2f8d7c4e618a71a943d0f30fbce765f0a81a0b86f000a9edca80be34d9fb22fd5959a52dff8b214d45dd690880833365f782eaa2bbdba3cc6758d345dc129399b5ad29e5d8b1f25d06a5eade8a0ef42d3759d662b6c3b27d9ca7f5804c2b3e92f1cbf128efb05252410af5cc57c287a8f8c6c3e7e83ac898b59003c3c39da20e238659b09f1df157216dafe77fb079590574e4b4123bf304f18960ac407b2405de4825cc00c35061d04805f2451cfe3e026bb617a8e284ae1bb816526a9c506a33186bdfacbd016102199b0ef23d253849057f884b691e22de105d33dd87777305d5ea3c564ff00af3dfa4db7f4b2aa305bda33a2476a6b05cce69d61d9f75f986dfc4527af3f19102cb5b69b2842d6d7e9e90e8d575adbc16ad8a3003209e911b2a11d4e200225ec90847799d4821848766737493f708ece7a692e4885692d011ceade273fc4041c5d41854376102a27aceaa0aaa4c3257eb59982599d817fb2605623931306009cb304bbce9135174946a02bb039b7995536a8c4dbb218259ddb2c1c5b9dd411f093e4e518d590f06f8c106fa0f4df4b40f10db27b63d53eb4decb2481d15a1b1f9a4dcf66168ae5f6d7c79354ec366e79b5875482aed5d9a686fdb62b17630dc56a8b87bc1dcb6329bf2d19a56110dcc82f939acb1a6e70e994bb94ce7ff31a3b1db0cf696f5c842eabdc0a6e9d5372ecb94426fc8faef668ab3d9576705e304a945cf241ce3038a11c6f5622a3422ca25d6d0040b99e5cc4e4b39843b7d72534843b4222447511a5174d7c860e10896b36b505a0e16e37cdf8ac5691664e1e5ea49b6f6f35384f56d9faa8b6aa800fd2444e741c31814b3bb2c8c6cdf828187c46187808eac8eed732b528bd82ac8f43e6db3dcd19ed12205172f578245a1589aff49acc083e14f1caa589eabd4d490f6940acbabb055c05319dcded45bf21f0d43708de5ca2383859008ca641deed357917f5ec7ba58101d82a5cc8a6f524f19284b599173697a880f26ba158d39dde2cf107c3dec0a9723aa76b6bad28134544a94eff974c94c041ba956834fb7b8a20cb3f9594b54e896704c00828401eb747c909f8e2fd22eab075a9078c84b13b6bbac635ae9abaadb151c6e827b606742f6ff2939ada26729759830191df4708924fea7ebfebcff8f927d22df05724b550de94617b485e6049826fe78c7dcef0a4b2d23fcfd60d744b89dd19180f1b06d00b6b4c79b71bd446267e49ceaf11951b2d9082b09d0e3489fe070a744a0e64fa7d24d7703e2e82a7f26381ca659b1f66255b7efd0df075b6ba4de7a085030738dcb43875953999c2f7d5392df24b864833ccc9bca50062a633f8e7b8150bb74b7a7c537b12c72b109f8c212bdc542940fcbe580040f301baf107ae48ae17da9f15e0d3439be0bd2a45788973bf8745d03428478327de779fb78c055f53138a8e8df3e15405872ddae7b1ce4515871a20b0787503e861c20abb64c517e0042a2680e7a04527301326182626e560ba34cbcd567a6fbb797077ae12ee618312c49cbfef179c6440930137664d6cc87902a61de833c0aed0a616790b8a9ff71c492e8944bc3f60fe2de37788fef01f939351f4b1fc27098590ebadd75746f60b012641c5ef934924a686fb002c329d97277967b2da3706e342c7418340cef72d0392b4fae0d4ac069893efedb6739f3bf6c4ccc98c201a908dd8f2c46ec94720c00cce419319dfc91c3b842b3e8591e284dca480d39268d31117cdd8efc4e520f283358f61913978ac3430c515e7517268d57226dfab2e09755fd0fbb8654723c336ff3932cd6cd57377fc84ad99e65e92e3e62a10cd8cb84d1eabe63874b70602ec92c43565c29787cfbf0eb1066ce59023d4ae6b066217bdc050dfad86e31c4e357566f631a0ec0d3fa61c328756f7e8e9d5b3b7c8f86cbc08daabc2e8f31bf8eb84ff8a4a6db3643f1495b21c73cad93e3e2fd220aaf09a29cf4c9c5864053a1331b441aa1c585625e656e3e58df740cf3d457c40b0429e10e67b7544310643409645badd52389b0023568f354686018d5cc617ca0be53ea64144ab52b5109a28e74e789766193593c8bad70d10b403c06dfd7ca3b97079f9a4974f563a19019c8c895f9372f50226c3b0976ba2dcba703af39b1c50c7a46b720ba7d6491ee9143c7b653930901b4617eac146fee04757443f36c7156af58c8bb33670bf2928276bea49e7849cc27cafdf183902bfbed16820c68c2238e877290b90a118068947b5a8977360e5a952e0036257dee5e0443433bc39b931f3a78a42a124210a51c58cfd80c56b67a52a3238b4a47524f9e269369e91ec03c1b7ec246c04420d0035d930d1a4936dae183715bee51356873764905f7e7ca33c3f31a56996e22caba046602b6b24130d134eccc1cfaa4eeb4a5eba6fa0cbc022057243d8d478035a50d38b9c15b98af87ef36ccfcb1a41265398b117976d06a7b604b0945f6724c34befafe430b6e687635eb05e98b27b9e57fc05ceac658bbf43be2b055ceb12385938b72f3e4e0ec92cbec13b6b816fa60b5404b418c33fe661142a616b6e24e8f9c3bb35d37e457d1dac5b9c74f471beaf7f8f4d26f608f520ccf0e113801460fb5f0d9b7c6ec1f81756e4486d34752010a0dfd19ff23b9bab9cdcfb1df1251615cb508e4922ecfab788decf45b7e877c73e46391f8e7368dbc5f351ebce144bd31203ce49b741425627c097ec248008f704ab6321f4c59e6e6c1adcf21d255b9c0abc5e5d1e913335b2f5c18fed758628396b06073450bdc8e51b9ac17a15988467eb7f3ec60395c779f28a2a1d9998f24515ca5b47eb5cc29f875c9d1a1165b3fa07fd3b99b18269fa72901035818a9bd02031ad308ff76ede118c97350024db3130d1c0b83bc7b65c3bb1f886c3996c4cd8939139aeb8e749c34ff131274570245f1e762ee28f9876c9e240e56dbafeda6fe7047f8f56148b0440f23f0d011a69b14d6dc35825881a3eba8c695d89526348a2e783135364552adcb2de2bd4bb42f48491930f07c20e89c9f87dc463d93e72a7162f518cab3474af3cd60888cd45bad31620195330ad1fa68c7b800b83761b351b440ee1542ebddff7fc14b018c913564c935e83398d0e28e5cbfd160707ca15b6e4efde70def302103524f2d9162a684c86e6dd17b74948e34b7523839b5557457dae8b84d9679010b82b4f9b6ccffc1777904eda4e4583e195476f62b6b2fc96fa400281d935327130216f637efce4bd7967925161f603ef63854cd24c1637de8a8fb2fcc7031bfba6108270c3dcf4e240a99c15115a88522efafbcb2905c304d35cc0db64799797a10e49bd25c35c762064bf13db5ed47baaf7dd7c0f4a4e768926eb2b5f9f2b7e9b0780258805e32665ef178dd88e89fbeb7c2dbdc7f4c7672d6cfa8a46075a304407aba54c51707cf93558bf6f2bc15e138ce94d38ef015c8abdb37dc627159b601389b40547fdd18c07caa2e8c156ac50719ee2e1e57d111bd79ae6d03a1297a40c36bfcb7110608d12d503545f26013b67f6883ade09afc1a9ec2287c5e0b4fc18961675c195b7025239a4f728b6354262a3b333a9f49a39f5f740652ce6fb271e03230bc5559de174f64dd0d90f02b5f2a2eaf004278215a9d856042f48cb3e2028a458428b511f716a00e9432a28ba15a6eb8cfd6a26c33641dcf788c1f6a71cfdd7f6043c8cf84ada544f719aad323ec1d2ee9f5a4888114e008cf4c467cd2b509709b141b7d822abeb2f1122d0735485f29454e0077ef21016ddcd68fbe9", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } - }, - "test_exit_code": { - "crisp": 0, - "folded_export": 0, - "enclave_contracts": 0 - } -} diff --git a/circuits/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json deleted file mode 100644 index 3d536b39c3..0000000000 --- a/circuits/benchmarks/results_insecure/integration_summary.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "integration_test": "test_trbfv_actor", - "multithread": { - "rayon_threads": 13, - "max_simultaneous_rayon_tasks": 1, - "cores_available": 14 - }, - "operation_timings": [ - { - "name": "CalculateDecryptionKey", - "avg_seconds": 0.111521499, - "runs": 3, - "total_seconds": 0.334564499 - }, - { - "name": "CalculateDecryptionShare", - "avg_seconds": 0.610321888, - "runs": 3, - "total_seconds": 1.830965666 - }, - { - "name": "CalculateThresholdDecryption", - "avg_seconds": 0.559050209, - "runs": 1, - "total_seconds": 0.559050209 - }, - { - "name": "GenEsiSss", - "avg_seconds": 0.124032833, - "runs": 3, - "total_seconds": 0.372098501 - }, - { - "name": "GenPkShareAndSkSss", - "avg_seconds": 0.226692527, - "runs": 3, - "total_seconds": 0.680077583 - }, - { - "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 8.500284375, - "runs": 1, - "total_seconds": 8.500284375 - }, - { - "name": "ZkDecryptionAggregation", - "avg_seconds": 49.366586083, - "runs": 1, - "total_seconds": 49.366586083 - }, - { - "name": "ZkDkgAggregation", - "avg_seconds": 21.116986167, - "runs": 1, - "total_seconds": 21.116986167 - }, - { - "name": "ZkDkgShareDecryption", - "avg_seconds": 1.465883083, - "runs": 6, - "total_seconds": 8.795298501 - }, - { - "name": "ZkNodeDkgFold", - "avg_seconds": 62.328322972, - "runs": 3, - "total_seconds": 186.984968916 - }, - { - "name": "ZkPkAggregation", - "avg_seconds": 2.200691667, - "runs": 1, - "total_seconds": 2.200691667 - }, - { - "name": "ZkPkBfv", - "avg_seconds": 0.336088264, - "runs": 3, - "total_seconds": 1.008264792 - }, - { - "name": "ZkPkGeneration", - "avg_seconds": 1.351367042, - "runs": 3, - "total_seconds": 4.054101126 - }, - { - "name": "ZkShareComputation", - "avg_seconds": 2.682164854, - "runs": 6, - "total_seconds": 16.092989126 - }, - { - "name": "ZkShareEncryption", - "avg_seconds": 2.506225536, - "runs": 24, - "total_seconds": 60.149412873 - }, - { - "name": "ZkThresholdShareDecryption", - "avg_seconds": 6.176445291, - "runs": 3, - "total_seconds": 18.529335875 - }, - { - "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.100550749, - "runs": 3, - "total_seconds": 0.301652249 - }, - { - "name": "ZkVerifyShareProofs", - "avg_seconds": 0.221828033, - "runs": 5, - "total_seconds": 1.109140168 - } - ], - "operation_timings_total_seconds": 381.986468376, - "timings_seconds": [ - { - "label": "Starting trbfv actor test", - "seconds": 0e-9 - }, - { - "label": "Setup completed", - "seconds": 3.0715085 - }, - { - "label": "Committee Setup Completed", - "seconds": 20.22829075 - }, - { - "label": "Committee Finalization Complete", - "seconds": 0.006707541 - }, - { - "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 304.143739958 - }, - { - "label": "E3Request -> PublicKeyAggregated", - "seconds": 306.698077958 - }, - { - "label": "Application CT Gen", - "seconds": 0.313315125 - }, - { - "label": "Running FHE Application", - "seconds": 0.003688125 - }, - { - "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 79.884513625 - }, - { - "label": "Entire Test", - "seconds": 410.2113715 - } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000001104c69c76ea8f5f6000000000000000000000000000000000000000000000005c85075d6baa615c900000000000000000000000000000000000000000000000eb6de0b1c015f0f2a0000000000000000000000000000000000000000000000000002a0b5c2857c800000000000000000000000000000000000000000000000036176084b0205127a000000000000000000000000000000000000000000000004549377f025fd0f40000000000000000000000000000000000000000000000003ff07c8a94fbdbbd8000000000000000000000000000000000000000000000000000185ecac0421330000000000000000000000000000000000000000000000044f1f17a53eba29b1000000000000000000000000000000000000000000000004096147c1d40da636000000000000000000000000000000000000000000000007c2eaade9aae157c100000000000000000000000000000000000000000000000000023ccf2dca92b000000000000000000000000000000000000000000000000e02d12f90edb8cac6000000000000000000000000000000000000000000000007c223713eba54496600000000000000000000000000000000000000000000000ec928a9ef7dcf2da9000000000000000000000000000000000000000000000000000282e9bb83bee61a05d33c39ba3812376a494cad4c46e07e18763a4c0cc37c7ac2f3635530c6110f00693d937276e524dafd5fffac373d40ca9ac38bc4003aa97fa25c28eaae471e6801e127832204ea5a325f05bb8550bd90338e17663499f9908e07c1a27a4d25db068d58486c5ed1d571b154613b2a444f9ef333d6490c341531f43898470028e20e0d46d87d6e6a8c672bd88dbdbae7c0e0a9d06da0323fdaf7ee61ceb7611d45afd3c8ec55431dab1b693939c2a7f313b832a28959c3ae3d30629655cc051ff1a5efe96161c60a46f240cb6ca1a531353aec9b48b52ad525bf6481f17e0923676a74fc00629f08348ca4b4467e32b7907ccf75e78538096134b580c00ad016d8f24d8b2251a384465ecf7da74301f64008beb9c8b71af609a0085afa8bb10891080d365806d9ed9f21901108254290c057a4b986687bebd3eae86d9d84d52ba59894f06a73d164ff99fcb0d13873279ce4f307eef63d2c0ba38c4624e95e009c113dba40ba429e2620e10806283dd5fad8df1b6a85051c1f7167194c70620f370484b9bd06cf6e6199ccd69d2c0e0db004474b4ffc62084f7697d21fc04f1518380c30df7edd6752046611f209863dd69ccbd4af39b35e05dbaa35d7aea81dac6609d0b9b649962dd23bd220709758af7743abf5395724f809100dfd19cb13ee772bc80efdcb985314bbe58feda8ac8cb79523fe9ff183ebf3e6835eff81084ebea45358443421fb0f2d31a11171c0537eaf216aa68a5bfefa0d02b1c5b92a10ac9238f294ee18007a6f18f72214951c398ce84c3616bcf3c79e0f2e2f022a0c90527061c47a42c43cace4fc879a5a326863a6f82d9c6bb420290aa900d8244a98b1668bcfcb6023d101fde9f2fa579bbd8947480f7058466d6000b38c5c1a4fd3aa5794aad136c957e92be80cca60120b6d839e54f1036f9e857e14746f2433e79c2a2df9f7ea53e60405fe812eb086dfac1f0b0c638610dbdd208a28f2260c558292be011ce1ad7ea6fc1a18428a3199764be0e5e4169e261b655ffddd06ca1104a26cac71683ee832dcd3cac2ca05e81de30bed5f8b67ce620c6f88f62d90a64a9659cd90458edbd62bb6cc9f8d942669ccb243386ee53c938746a2462ab0830a5ee749ccf6259eb0ddd308297e3093c1e2bf8454dd5e900345907a8608b9832d8e40d08cb3dbf14939398e8c032b1baf82f3b45da26fc2aa8a69639d1379060a52801809840916499ab8679e0af7fa2b5e549f58ddfc8c60090ad5d42fcf59ab9661b29f6aee13a20e279602925e6fffb2328ef1334585e819ea8002085571a91540444e60b8c9350331f5cdfe8ba889466f4b26343d3b179b436d1015fdbeb6b1586fec95a850abe17e3d23dbb519b068c9c59ed740833e71319db516be4631e64a10edbbdfac8daafe9004a1dfb5631249e558132163eb6429c715143679b05e2edc9c236a9a7ca760761c3a117bb03755bf8d04aefc7742ab0ba811710087242ad211047ff4f6b9c4f8faa4640f53721a400815c301a8b079d1ca2b7184bc65e7bc3cd2c380df2376ec233411c4d429fb12795cdaa8b932bff94628703868796573c773ba5a60dbe2dcfdba86fbf73e2cbabe306511dd6f71509607ebc04687500585f37d8135ebac5386803439c01b36e449d3b029c1576fc2f422160f4fa03d8f11c89e134103a7918939563aad5285049c66b3479d416852b11896f8b31c6d91580c0cc03bd04ef40a9b201d686cbfec6170ee5a1aace7c7f2248c548038c030a7c267fe72915cd397e273c42561e11410672a1e41f71827eb247b679c4eebf11bf064f256f418ef2364b7eaa29fa9cfbf018eef1952daebd915d423652908cbd25b28b3b07d28cc918af5ac73226b02d6340d02ee74f643db15c870a4de33c870580fb55142ed86a7b1efb02d4f5d1d6e074137126f61818d0dce8e49b8aff13f2e80fa7d585aff6147ee7265bc92df749635d9cd77bfb87508e4dd08b4f437bb59702e34ec8c390bf6e7455a4619bb3586688b16e1045a9c0c8d0b760cf3d11357e9840fd3e702cce25045c7835bb1b0ce6275af5571d18d1e90731123ccd8bb5b7550b8ba901f3e94958acb3e9583067cdaf68cd913726817755888db2a7a28e1a4748b9ede248fe0ef76aa51bf8e5eaca3049ac9dd7984241212bdbe5b3d0d4b586a7f468056893fbdde4c94203e8ff8238fa1a028ab2b059669f73b36030f5205fcc5958017fd26f9fa8d827b6cf11aee31fa922e8f0c21c5cccf059ac7d29e54a4df6f1e43b8a4b22689859e8569dc98990b9fd1d1b2222e19e76cbb31fff18fcf40798cf42482bc063795fa19dfe65eb6e9a570b4c41e85f9c94ff56701adabc0cdb76def2f5770270aa236e1be1c18ed64e51cdc911a1133e096a0272e0675c680fe24f2cb4f9a201401977945b920b02e60c84f3309e8ba8688c6e982bc9357327d438c5d34986a3ee5c88fc5886a4c66f1d14ef625d0aa01c82cf320cb1543887eea14b4481733f213beaef47ad92251eb2910bb1c23b4c930200102e6ec848b5470cc88a0204f6d4a9aaa1cb29e808e3cf2cc7622ea3e6a331b26e1d2a78f90b99066780df63afffc89bb080ef954828fc64a9b0e589c3d02f1f8f8c03919178dcd7138e1381f54749a684c6c7f852b0e85251427b6dad8334cd11f9f2e16ab2d7654d1dad887e5dce9d872887c6dc9d07f45481835c2ae47a1c0c9b680a009ce8218506f77c3b8b62654b8fb93043f60dca8992c4ca79d52dc8431a240cc5d01a027438fa19c91f638b7cddd6db1a1f6faedbe1c7eda2cda8637e7e0a2f49d2ad0109f7a5bbbadf6c8478ba78e716fab26944e182e437f00afe8873f1030439bfed6f6bb3abc39bd1882eb2b6eda0078c680e924d942cd5b55449783c3a8f192e24aaa6c305167959fe1cf58e9065a7784325c2d0fe06c3e064f99ebc1570a82cd43c33ea443971e0f434ac11883fe84461b9821746609c618a3362bf051334253a22ae38e9b6773a528204c0797297835f3f61549c8cd8ad16bb415ada181c52d843d558dea5cd548c3823ac4e3d6df1838e0073102582f62d7dc0ac66710e9d0e8826ef77755403b7f12a2863faac6dfe4cb1914e29db1b956406e9f46ba2653617ac8ede05dfb69035dca3ee5c2d00d3a612ec25b4fe71e06659ef23bb53a839b6ee48c4e38207109e68d3e073fef16eb062dcc3a22a15afb381581cd7e9db2c78f2b801ef7104a5ffb41dce92caeebf6141190682aae5ce74e6b6adc7c901eb6db76c548746b9170a1c81930149032b19c0c0ab947223f7d82d1f7fe5930eb75cb6dd0bf5394a906376146da02731747ba17ee40b5061140ddbffbf7b01d0e6661cb6d3cca9eeca0a0c4640c5d61c72d9819f1b54c8878b1cacc853f1ca2e3ef455bd082bf5bf60e4b13ba7569e7804d5b1442193595af32b92e91b90c7f7c9999f7c665a9c7a7f5911d73d415c3339c6f0a968a68b6475c1dabe40dd46087630e1fabf2155f2017e4fb42180a3319e7d7063c027b22b6b8389f83c9aba5a25fcb0cee256c1c16760db9bdec0c82d90800249d026ad7e4fa1f8635c4ee66ac2ecc8ac01ec76d44981128e4000cd5a529b4011b6145ece1a1d5404a453d8f28fb6ecedf4584fc8a952cb6f9d6df93cafcd21eac2a8b63973d1643a07daf8ac82dd5b054c0d25445e09978693ac63ee2de6516fc2b9c0d2c59aa561b6029edfe22e85ec79edb34bdc7617ba11cf3d4c0f02b13c2eeb3c0faa2fd0592f8303ed4bac9e9f81e7c627493f4a334c539b8acd96f02bc66d56f149f0139979ef02b7f584ae703925e664182e410729af2b9675cb406169d27e032bfcc6e540bd4b7b1b11054f83ac31d5c5013e0ac3f6d6e6a97ec1b08b22c0d8fcec02e23c3d2f56227fdf155a9a7c363854deaecc5321bc94fc52e50df9ac72c04b4fb74cbf7cd86c427f9049ea0123dbabed674a4e2de9652290ddbf03a1835d5a92ae4ef3237137432ccc8146c33fbd50155eb4e68cabd0208196843c201f2ed0ff53d389b7845fe62eb1e873c6f5c9c1216ad8ffdfeadfb1718e74c50fe500225ee24b08374d2a8b7fc17d324f50220961759194b39f844b12739e80621a5960316675859bed49ede417bf5a5acc2e4b364b70bb92683986c1df944a9a3b8cc87e3951e156af6abca648b28be2b74e6df59df0801edc236b129ba40af8c1c9f93b26fd9a45d102fa7bf90e7f51f14dd59d241c4327642134210376431209d2593785a6a4a6f042505387ab87daf614bbafce0a71426a7e26d2ebe8f7a36e23272d68e4396afe0eb899fe01103e5190ad987834ca9971bed9c1030fa67d27207d4a97e2583383f5904f0a0ef868a6b8a0a2c85fe1c9554e620130e6c08293a578b5a28ea7c0021ceb00c5e4e171a08f2097216d3dced274a6407868f61297f9fe9e95cfa52375e1390c2a034d849bbce454638828ee58aec0b0e171dadb3c2ba34e649b6df427a1a22ec81e2406de5d9d3db670db9198fc1070ef752419ede5f9973c2105bfba9ccb21d5e02ea1e13024a8072d8d2816018970bb8e226425c4c82771c7400941b55dac1a82c6ee13100d8bda00bab5a6d4e0111f101af580b036e177b7e88d70ebf1f57d3d9869f488de7ef2e41923a71cda5153c0f99aa741088d700453e078bda0ac7cf72c4d40e5755f51e9f972d5193161ffbef1f56c0eaf0507bfe67e000aa14c77e2fc597669522fd7553bc3a340e622190378956119d6213836b644a25648d9b3463ff256fa07f431fb1254f38a2540d7d8d1ba4d4000d5470105399683fa97f32adc17549946b7d4e33a06085ea451bb9b0d600fe3202fa84f96bbe9895918438d9d23440f5d086cb4624eefc7a2c09554cefe010fc8412f7dea624ecba0b778302ee742b84e396dc43d3c76eb2b10567ee21da53b51452ef279344c3f4cf1797576436ef727f496641913bc0f28b2dd03f71184034d502788de0d7d8caa15ab98c454d2676e297017823c35c061419bd9a63ba9deda91b4a7f5cc27883af1ce981a40998cb00af959ef782a4971b2bdfd91e38d7045ff5b399c5c097e014ee9959edff45d11affda024527b1c03417b6609dd14487de0f2ab328f2c7066e2fe3d18fdad528fa097746a24139bfcd1361368052ae8ebd22969612d971a162c5cd161da46aefa32d4b540937b80ee404eae6eb13a39ce56f8fff92b54da51403fdfcf7946dd8a43ce26cdccee842af2f6eb15827f305af101464d910fb943488e0e5ba33853c6206eddceb9e2575bf15ab59f5b519e3023a368f7c1584b1f9e4e185f05cdf6b4ae976f075d4db94882711c2ab8e50b028f8f181b95604247e18cefb32c187176e0c4264b9d9e78f7c053277dd97cae1eb03dcf68ccb9c14d6f77256486d60a6ef3c8dfc54254ae5d317284ed44a8c2ef6ddf4aec97a529d841a43fdf3bff0d07776383574af343a6b2504328a61ae7607b1e792ad94fd63e0f5f7a714febd8866ecf4e93be767a16f2c6a4302ed3c88f3e59bf92c5343617ad9051655cd702ff03e0a52af7f0759fa18826dd0d6c0d6b9b58a943c485c911cc2e83d683725ff800e4f44dea9e5747c064f56dec7c38e976a8667949d972a1e8d5cf265e4f07d86e2bd4c2706fea5e50780f6bbcf004fad574131b2a5d060402b25a79ba39adebc44adec286e2641d02c06eb8264faccb1c5ba4cfe130df3ea472e3ad9df249f14eb4db5f2a7b1bc271d67455ab068c58b17a0eb2445f60d9788b899e3779594819895fadc61cc2e080daff29cee7c04c152928572b235c790f229121bf087c155efff507d086f273f11769248554252c8d5972cc79eb93724ca839565986fd0d23149a7d428b2eb18206374aaf52de2629a8215536912ce04303d8308cbe65b8b76a963b097519a531615325c2fd7a1cdd84daeaea5f0951ba4421342db3b0225bd35b659e31cff10180bd6dc65a34a851c7c9d943c9a600c76aa179626a52d8e9f77fb03b5f03100037dc56ffa38081b2a6db99e9299075f3b2c4cf6039d9586cbf123bb759bdb130475385704108ec22012b4742792a05037da6aaa56e84da60c467ea7d3a908e81d7ecf0f95150be7f4d5dec9a1dd9dc832495542d1c376395c5e7aa334e020a21b28b9245119102fd119e81c340311a9ae3749ba3384ba0f1204f6293e3937dc0eeb2fd7a2ac724da6224dfd2dbec16d3a3af4c70ef9ffc653a52eab8d1693a61bf5633124ef3a8eec373e13373abc59d5993e616593eba7dbb5e248ff802881102c6eb060d5d2b8968e9eef372763ca5183b873e5a581e9e865f47b6028bff71c19cd3c08fd68a23f0d9c99aab607387b9c6ec678f9455616311768fa832a5204da68b4a1d18935ff4523e6cf72b1850132058c7f150c2e126196b6723f19ad2fb8db2bfb259f034348221b3d3f87b85780619a6d54e67cf0a55965862b7fe52e23144adc595bc2be1a48f1c1cc41b41dd9da0dbe5adef141730e340d04585616328ade11c06b5003e195f1e3dab9003fa8013ffd3f7e6a67c1f51ebf65dad418a861e2f11f2ffe125ce67efb0eb74f33bd131b5a5d11b6446a8b3fa207ed8b2285a3475847e33e625ed4e7d6533778b595aff6607586bb0412d8210fe340552e3583fceba101476b4fd039726118ac87f80df3cba443836dc2a53fd6fcd34e0e18d5690c79a9e9db8026bb60f070b81acd43c1e5f7030ea7dd2d61bff42ea430279ed1125758d018a344fb17c2592a3d2c34594303a65937825b43c263ceff01c029ef10cc78125dd9f4a55a19c460e1ad391a367f976c8d14a2fb91320efe28825a32c3b76a082a9e42428bac2e25c4892681860db150836148b3b1fdb13d1f64fd78bb318ce7387682b34c821bedafa127f2556a9c2687178ebe195756f609fe81400fb471b4fb1d59f727b378ed82d87dcd5d496097eb293d973564d398058ff3208fb5c9cd219328dff54018be2374f4a66fa489fcdff636810ec5288a0f1428e907d19a12444d9075431e049e594078e1d039e1260075278fb374a93e0105ee257808016fb5c76dee7c048012653bed21b536bffc786e8a1d77a2b8290b41964caad2025868bca96f50308b0e8d7a814ec83b900c8f77583e86bae18a18d2d335215664076fbe4a32999c11aad848aaa1df092c39ba98380a3439943e1ee02ed6a5098b95951eb7e0447b7df1eef98bc02b65dfe974bb92a4ffccc41c2983166df1755e1dfbd5545f0d62e8a27daaead2c734a6e61a196f7c253b967c1528ef00362014c7cae847514cfffb599397f6fe6c6ee72025414d2908b47de40a84cb4e3c3424ba0329707761ff4b3262835631ea1a3e164fedf5077ad2ad20039921b628ef95d8480eb14371130e00af763739537e2d02d6f125589f25eb850ec1140ed345fc070944b3846169aaf337e145e157dba7bb5cc8ae1919e050e01864dcdeb4f64782d440a6e643bd1f58b00fa425237d8a1f8b9a014d7198127601a587ed01e73a590f2bb886b1526b9aa2f577b51ab869e9e11fb795efaaa6c31762c2ab563613d28bfb2c3d59f769eb526bff8eb35ce7a97fb655447093663f1d1095d270f54f28866027ea95f63a6e5715618c99e7e08f17948a1e8c65d0f223eb8346f3a9ca6c43ab9669df0c286376cb610b5a073c5371c2c168935bc1a82162a0e476196e2da58d90892d41a9a9ba1d51aea5d2e8fe1d3554036501ad561776fe8203c0febc1bc49c023b4f2095f7ab99dc9fa4a09c70050ea6c1a6f93d04e66dace55265374deb889c25cf2a56f3cd964ae8d3ac45ee95781743bf701a13b4cb3c6948375a09050916d900fb3d0811a077bf61fad07727b53c2564dfe216679c8ff255da315e1e475b866a908dbbbd515d620c7182b849e8863869ddd808902283f89ce762a42304280edf68f9a76b67fcb61a7a54cda8575384ef380b24d73b1e75b50a6174ae6ab664e0a75f91d4acd60402170a2bf967c9862f0ae40ea80f4d02e46dfc9d22f4c5d3358a3a741b39930b017558af7a8c9e268f37a00cd48b6a5afe6dc0785efacd85eacbf38ff3691f503bd723796d7a2b7a5505471c0a5dfa4b6eec5f56d4b7e4805da697d7a7e2878d717e21d742f9699ac0aa802f8b8c774afd950836e8ec22a520412abd775c4619aafa9dd53a9dba31f385d121f23710baecf82aadad99712d54fbbff07e19d618663216ebf13dd355389ed70ab297bb2e8f14c44d9db5aed00f4272956f940c814e02c329cac3b33e110f7b260c8b643c9225c725f638d415096330c953d46a6e6901548de47f050cea00921c406ed69b8affa8c49e5126b09b7407f1fe39e6c3a67ff985f44b4068d17ca2211a8e6004ac2ce57b738ea79b47a79212fe05f59fc333dfea5b973e0189f8711d2ac25b7da815377b5e0b79bb57edfeb0b3e249284b093f937eb3c8cd5dfc470e799e62210ee5038ca82113eb298d0a91ed7265809b0e3c41842327227bca7e179281876a2d730f88bfcde6a261d12b63b325fc4b8f8a07deaab29e073e24612e721bd4dfbc7a76064f51b97cb8c9aadaebc2219602c4018e412558f0c92cf50f0300b41c3af93217af662fae6c20aba5c5de25678a70e7548eed1465315e04260f3dcd91a642d67ec3157acf46ef336e01231612fd33a33d547b352838ff602202827e24bf1d82e874c4b2e56d6da2211d4f7ca4affadd0e17c9939bae8c861988b22b4b7bb9ef9dffa40b919b3a3182f30d07eb113eb168bd5c30bd8b206a032495cd76b9b5ded766b724b3e8a955b114b6bc8eb936462efea06162c87c2c1caaf7adc04449a6a314f788d3f2e7ad03a89388119434eb73467f6a22898b762192c947c632f83b1d839ac813849f7f22884a21461081a6d636bc2c044241732a226e71541009557abe207a50bb07ee6e5318c4ee2f10bb0bcda8e4fe00e6b71fc41469002dd8f72c043824f87eeaab33b31ac8effef573e9f15bb24f1af840107e0034eaaa8cce6143056e561c708175ebd2bd2b01f7204024529b36328ec107e1079b23231ca0576310db1dcddae6dfb2272b968d23ce655bfaa3b5b578bb051aa676102be135cff656a57bf369ca177e279e14f723c6d567cfa0cce54170301665691ed411acc68341b6e22d99e80c111504fb6e8fa565540666a6c139f21693e4c8d39fee103747a085926e9c36d672e0950b53e0168b556f7cb1a672ec12a0aa0eab6371061b8532a895927a22ff8cd0d1b0f4c4daa831cfdb62551d422abedad3879ba0e5d639f4ea58196f23087af2ffd496a7572303590001f57c652e0d004ca35307c2f8c99a335d6378a3770e5dea4aeea8e5a4493369f406fbdd1bb85993f3834486eb72a18278e27c76aa92986a4b9285143493f120104d628016e1f540e94796eb5b0014ee099328f84ceb614add86ccfbc550f7600f3413651c6d6c29222e50e8a15edef04969562fdbee5ffa843960d92d565d380cbe1def21d8212fb0ef618b09a78a0d3030ea274197521f239b7b82874d575222ec0ba81ee82dca840e340012136ef0b243eba0e650058aac4a5fc88ad5327ad848ecc710d71beeb1dfa88fb932d6125e181271335a9083d7fc6e13cdb92d1ea5b3d60912591a5d8eab8b8db19ca4336fc32c3147ffe8088684fdcaebf5f634cf5e1eab1b68a4d96dbdaa0afd40748e0b4a0ebfba0a627f30c9dbc621e727f7bef5e78e245f34dcd0a1828753e5723eaa14bb4313dbe15d5e76965f8bb765910f417ae403a931a5b653d4fbe31332fd04fc6e69626fb834cda6fe48674b98219ec25e212df043b86786dd8b06d34b0bb7e6cf4c5bbaa01158fc330b05fcee8b4fb9eed20ead836e6b8cdbc6cbc06457ac64cf3a72283181e35a9657ba381ed47169ef3f178a2f40907c36a2de63a99c8cbe92a23d9b10305c3003f5341ef1f9bdef0a99000586c10ea5861a049a13f7c69b0bf6526387aedb3952554218cb5ae0266b3d17107b45cca47a5078acf00a36247bd987db1d331c585953c27b3e7b68e34c5c086f1e38aef9ef67f6bd98ba574b8534c2f71e4ec038565f0b24c6faeeff168926ae5209a04acf180a8c7481af21deeb7a02886f61625e734604feae11bb965306c08ab20127db181eb638f57097f85600f251529faea4299afb46dc539ed447212bbf694211232606e3fc74627321d1188ba6d02f9e8269dd81843182e41575113b3b7b2c3b1a8698546d3aec50985c37a3184edcc69efc421fd3e4f939fde4182255831a27d9b5d835ed40def306d138225b855aa0b81e5707438d4cf7408725dd70cb357f50ec938767a6216c3c45ef2034d3fbc3c8555a67b7dc78b47d8628fd188cef3d2b8dfbf4fc38ef79f480b2efd8c4adfd46fb6205e251212ebaf31e70b2d8a82ef799db3afddc46ae635734ac0c700fe84891c053e63156981b0b0fdba632fd2f3c239ee4bd04254130f5d66e68515143c5f4d6f2b29e17bcb1a303aa93928c09c5312a4d16946121da973879c2af82cf71b14a2ecbeda0e5ec8b02462777d72e300a2f695fe1e4440becc12fea0e2ca14ff1862931e8175d31cb1c9f8844549a6ea22385d3a4f60cd14fd6425aec8890bde9221d42f60e54bca41eea53a6a00c6fe7b66f129dec025d3c05b1a7661322b803efc62b123918b9700799cf50fecd11f0787f2807bf01fbec1f9774eb693afa70d652dd59d292b74809f0b45fd23a36228017f8cfaffd39368d2317dfa86d5eeefabb69e13c7d7ca5128829e70548ddef6f0d1230f3674f752890c754e20a858ff699eb425595132818423036c330b0a4c86f547594d8e34b5abacc9933308d03ff4b5307c9f44b721d9e1d6655d4140a8adca0117a6ac07565e7e8887975eb3965583949cd93c18900bb301068bcbc64aa85a14a8ef8365ea3f57bbe465f80ed8c039a04a7c9e5da0e09ec9c44885c8ab72021e4f8f6c388240c577957dbdd7f64e929aa54c2960a071e8b4cd84f4f659b95605e75c42ca42547f2462b36f75b57972e6f9859d45f067723d028b166e84ad2a5d9921bf762376c3e90ae6b17bd87c27e0fc007a77d1f5f7d8b0028999ef82f084a250d8562793aa0ed27644ad82feb94e80d948a6f09feb51fd95f0632eb2b356c130b50c4f70fb0f967cb7eeddb222c546a5efb49050476a0d41bb023dc5d85b1cd7776f9a2c3c792be49704ef8e0bb09b9e6913728a0b8490ce3a1277aaa62aab1f07ebb41c1f395c34eb9e983884e8dd953bf4a0f0e353000cb746ce229ae769ee10c47f15ec40591d589d798d0c2bd2655751106774bd58d59720491a086b4721fe745209895969578d037e71ae982f8a9320322a859297e7a85a2002ebe2e883aaad92af39668cd0798c08b7eddd7d503b24b1ae2daa5c4eb25393ee6ebf4008bc4e0c072ca90b67a65c586fcd5550b29423f093d0da983505afaafb1eb0771e8e9e1cb3539fbe61f5efca2d4860b1fa1b36121e5effbfaf7611b946208ea448d990b3cfdaf50b40063af0b7cccab49f5c86b16506192e554f89afa326ed0528f6aa248eba2da1825d58628355393113a8fee286a02b1c4a452f8f15011bf27dfa4fd6832f09725a7219294e11954b957f9221166c24763a83391287fc3eefe14933c5c14f4c7d6937f7b75542347d04045a110890ee0f78b861b3af29a624e25b367017d565f468cddce5b7f0cf3c35939a710bf47754178cfcde50bc00ad8afdeb5f65ac80bf3130f8f916859fdd0d676b51a19030af15d64ddc9aefd991cd5cfa036cb80826b4373b9d7bcc892d88b7dc91054be1686426a50d43560b6f5a738e11ac395dab9fe8c808f8f8ceef391350406b72a33744c6e8340275bc6ba889bb2a39e4e44a5ec3608ba7e9ff3169958a41fdf27650f39023fd280fbcd8a14f01e01129e55d1f39690748e99810326fdcd20e052a932671c905b1eaa5c122a28ec8ee55385d4230903c923a10347dfdee01e17b2763b6a7060a5b8096bdf84673c406e1bbeb5fba30c109497c0aa3ce4d91814c363d790b240b21d0c46bca0a903c5fee1e5fd6e2278a36d7209bff11e701244171b2ba258a8c4b611fe9d842c33b90dc2bb751a4c9834b94ee633ff2f0c17e99d90e05e03e67dcc81f0482fe23b3edcf8af33abbd48817fcd2f01e5cbad02174e2613f854965fff32fcfe1b2ceb878c5e8451dd0aa5d75a96db1423038d224a2a1f5665b42ccf0cd2009d22d026f92aed1329fa0eeeac0bd789e8a094c82617ebca343afad708cbf7746a1a7b486be884219e41fc54bc92afa7c4fde27204e3b778f1434022e9a0f5af3cb796f94594c41fe3d1343ce8e95cfa3405e76024841490ba15b8d6e571c78b5fa9bdb4f42cf6939f0ca766f8a33f9af1388e9f0cb4e92a32b3f70b729d395a955e2080b6f0ef0d26e8692148295a7b56b5f6a31596ef09d919d4e9ff98704206e61a8dc0a9ce4662eaf6a10878f3180ecd6bcb0093e2dacd06fe78b044dd4e2f973281e38c793a05bc453015dd5d99c1adfa70234a32f957d93338fde57563ebaf78674ac0ecfd3bfe0a8293a8222791613d2c2ffeacc050d243fae78fecd4172ef8ac8d65605f37137ac2d95706f7fd8f331204bbed683e9949fba2e4bf9941b4939ee707c3973843138bebfee49358309dd60265751d08c2e979f56c6bd140921e7a66e34962c2e608d7326c40f95b797636285580e22fa6e96ffbdfa09b8be7d9c5def6bb69b24b832097fc8501a8ca707c0b7f05f0505eb62b7964b96d0c4913735b728fa4baf217082e8f47e2f582872412c7a05dcc806ce1d8cddcca1d989321c22e660aeba3d8945d7f932b6de9e6591b97a10e4e7f2ac05c0135756d3e162058d1b9e577760bfa12f779dc1b21c04300b8aa9e06aa51ffdcd11ff50c67a307276719bc15611fd86c8098faf7c829da2c4b1f4eb53f6fb595e1961d38512f3ce9827b9e4822b90fe6e5c0c52672cc1d012e591b62f714b671321c5ce756f826cdc2bd630a9a79272f7aa02baf56779d25d42ac69b671532b1d51d23e7c79160bc322015ca838b4e674eea5167aba83c218ea51067f7176b027556fd11238b4bcf8865d89a9cd0bb3e4f43477398ccf22aa043b2ff7867dea2472de15e42e20bfccd0fd02248e6f1ebf7f95edc2eb2312c0a6b7ad4f50c3bf03555011fdf65cd0e2fa85d3bb7df6dd924e7bb5794985118a1d10561c8f03828a912b04d1e50f8bde69235134a4d4ae2da48c118a5dce12e703a9115055cdecba78a75a6d10a5e56613e9dc3efd226cd0756e592f880642c9974ff67e8a91633f52d02e73ef44399b35cab4796c37156f0f9800b7a88660345dcbf63e44f1fa2d1f8eab547a67f70db529978f40217b5422eed2588a4ee300457f11907f9415e349dad8cb86d9910eebe588cc8c0cc798a522e39a669031bcc7172286c9537143a92c965481a89f3c4e860d5ca9824175d9c7a597ee7c61c115ec99fde4909f191e31613746673c5507ab6e01436e6fe5f07d8a929fef51ff7676cbb3def22e44a545acbb9aaee1773f706fc8684a7b465d2f92f8af7dc0a1800216db2c08004b42287c6df656f6b5dedaae8e1b9013c94532463df05952bbf330909de4d033727d32480cae73d2a2e9e50326fc605ae30c0c9f011128b1a678d3619cafef8cf5d5e07d61a849c05e91e9ce8290b8e0ee69c705736ed81117bbbb0fad9bfc93fbeda8ec8b9464853ee161b5fa9b5e954c36d10f7d2e01d0837f15100dc35eb17b2dad7ccd87b482048827c1716d510244380ac5b0934e3124b0552ca7a5902213329c982bf38782b0aa337a618d658aec9d01e4885977f1514eb3c815c8ac482050a2d19638a584180f196c16339e4652b1b7f1ad870891d63a0420da9390f9dc04588fdfb066906fbeda08ee28cf7f06541519f991aa7125c2099036a614739e41d2ee4266b3faec273304a8a7fb09758a64cdae16d281d06427553cb1acf30f1603889e575889f826a832acc44180f68c230647d971413a392f060a527885509f15ad448f8aece5aadc9836862138a8b6d3b52c33dd41faa36de1e5f4355825cc13a32d55f9d84a7337b691d12ab95d7eddcdd3f72a22ee84a7b8932c0aec98b47176220beadc273414c3a7d739e598b932603544fab03f191cf63a65ae49a832011f34c94d75bb99361b17399b27d77231128adfb33018fd3f19e9f95a38b4f4296e1b8ac28f6ee41814be442b889ffd99b621ce5bf1bf69588a55456a7f9911222a4825f68d22106aaf2ee07891bbcae65d952e75b2ce90f558af3f890bff0115d005d1e5f714935cb35aa8e12c3b0f194f53ba20427e5d13527c1a9f7563f61ae1f59a6dc16f2cca1df47ba4d47ad490a54d1a9af10e1bfddf43ad707f810b5b9b48a91b16d892c73403c3f5cd6670bf79e355e1d1cb721ea112226ce6a6d52283c887da649f153a7790ec04f440176987419b5421b7c694c646cbc5618e0f2e9d9ca5a10be1c9e007d519aebc3fcaa904cf4028d0f15c92504ec8655c6f5ec0bdac134b4194ab85c505cca657e295d2041c68be114b83a369cc7707cd41264238945dcc4d2581939579ebdc673ae8a5ec925776e28341d66f9f6eae02473c10b21c2f682ba22fd78f1b25895ac2adad8511422110bdfcc50e8b7e10c22d1061ca72d0945d2ae76eb874cca31ae41ce92e335e6732e0ecb8257b64e85774c95c601b69ccdd6cbdb5bce2bdb755e0f4309e735725f", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" - }, - "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000dfa79688268e94f45000000000000000000000000000000000000000000000003b27d8c4969fe006f000000000000000000000000000000000000000000000008596197ae31284d9a00000000000000000000000000000000000000000000000000025071b1b78e460000000000000000000000000000000000000000000000049f29ba73988453810000000000000000000000000000000000000000000000037166cb36624c1ddc000000000000000000000000000000000000000000000007d050eee5ee699fe8000000000000000000000000000000000000000000000000000258bb7095c7a3000000000000000000000000000000000000000000000005704f0d79a04cd41f000000000000000000000000000000000000000000000001cc8608e7634392ef00000000000000000000000000000000000000000000000af88bb11f4b13a3c200000000000000000000000000000000000000000000000000029db74f7fe7cd00000000000000000000000000000000000000000000000f4a5900047ae9ba6400000000000000000000000000000000000000000000000a2f7b686593ac5c9b000000000000000000000000000000000000000000000006e5e184d039f43481000000000000000000000000000000000000000000000000000265f972d900ce28738b09735682ead7a4c64799135201b3899d799236623c3b56cde6eeffc14c0df0717f0a39df3d76c516e8bf543821b9f4ba63b14188d420b92f5ed32350042061c4c31ac25d95dc3913a7a4720dcbd0d2b502eea4245f0e8e0b648eb3664f0ffcf0587456eae3287e653a00e8edaaebecd3e65d8ec98782517ef25ca5f5ed17e994084f3bba80177c052107cd4673933d4d7d498712c3c1a765d4ddd106cf23121b833a7165f5c9a7cbae738a5033ca28022a37b8399326468eccd785a1650e921736cbaea5f086a1fc0b8d34c9b3cf259d4260e3a0fb734e9ba562371b5d22be915dc76a01693899c6a34254a2ee1d2034d4b4afb734de2afae62fe3949727c3b05c1e3ca04b6abb43df9172b580ce32a1ea52c6a13bdabefe41b291b61b0acf905985d7fe37c2a12230fc1fe3c698d1495d937e0315f8ebc4f08b81f07b054fd658b021f59f1486e8105180cf9ea58448782cea621e76ef6e2b502df53c097f54506c667839a3fd6816ce0201339146d1334deb91f8ea6a14405ad6153a182d352d88c84c0f0d3446535350ae13ab7965e65605009887d7909a2fa1d42e2896551a0829706edd47b1b5786e28188506120cab5c15268750c76c2282e001032b6979a88d90b20a1db9d030107ffc0677ebcb6777c7f4ca2ee0510182362e2657252207babe037b83a8d8b3dc52cc67e771540a593d889433e9cb38eec2d71d52810b29a729959f506e4ee054bd6382f9acfcfa7c288777d3a5f4797e9342229726d66b20423a7f33a255cc294690bea014d9afb0331f91401058a271c34d2ab341191877abb7207f8754970389d7dfcfa00ec4ffaaeb0524dd716cfe7cb11e9223897133507d5227ce3ce11671f837ab1ef85d0aa6e308de0ecae5e49f712bf468ea81ea81e5527ee9e0f1a6128b1d1d32c56b82e3da07269e74c46c9978078a4fc2c3ec815158339b79250151302bb388375cdeada8e2669ddf041f19621eabb305f4d3270b826fa6ed2f25db9ffe39fc47e81839abfbecd91abc210133053d60869e919a115fda8d5046e59bfb2f874261d17b698de9e524a7145be6c521e3e2c29389e805e67940c921c0dd97b619b10e3d3e1822e3b2906d448ec85a031c38b5d1f34c2d4b5b7d5b6360c16fc3f097a28fc83c272b1fce5a6398bbe20a3997e59fc3a0e2317de98fdb917166780523993068ec1cdcd514df1fe9083f1991ee90b63c34c18b2639cc3c3df351ba4b8ee53f5663219c845085116bb81c1639cac4dda73a16cc4d7f9e8e607200c85a8e0fad44becf3891ffe93aa770132a39310b85cbbff14f0bce323cc23f445f6a258707355b3779232a642eff4b1a09e4b99928bfd02b699bf248fd01e881d98aef5772a6043cd5619e1a61af8b3303c7e35dd434b2854019587951ffb7e885f616ef360789979c7851ff5bb479421f42bb812a236b6fca6f7ded6823a28d36810b5eac6732fc2ad4410f715951cb2b5e09eda26e7793afd5c7c865de1570c4e72d9a88ce4b31a88264717a070d802a817a31b2c84d4c1f13a918c3a73c88d8a67f6c81fcf41ecd278894e88485c5062a3ef24ec877338c2cbafd1e61205fa95e2b93c59326cbf8f6468453a1e85e054db79eff791b9a1cb823dd9566f06129dce2b1f1c15e9c6d878629d40f61512eced624da055ff54fd71cf3520e5a759b33d31ce3d711c6e1163e3645a725960c4b1b6a5254cb9b135176e02075b45e9e1ea54e21b1a420b1414abc132d02be133cc2dfd9cdbf2a0a44fd11b51779483b4052c658b345b31ba93b918522167102cd4824015fe51e5b84a6fb335ac4842f9ed11d6f5e69b4d8dd8ba64e0f36270f78b42c71621f1501e1ba49fa4e661de16cb8dd3c698093969b8964809106412790264f0ae9b8007b76649a949b69ac1d6ec2a955902ececb7d33336b485a7c2c3c6a1fadf0fd121f79fface6cdf947dae0f776d069829fa900f514ece4141616bbbedf7de53e30cdfab6d5619028b321d44e93184fdf01e7ab3f5a4424386c23b8e5447413e6639c215e2f7bacbbe08458fe90fcff82f4dc73b1b9fa944e91108a5c28ea9df2a9e1b17874a186d1d5301848073af15ed52a4836a71b9576fb284357773419bdaa332b8130670664dea49b96012f02b091e273421d579b8b7c0db2b2715658dfaf910c32aab84a6eb1daa3f8c4c72cd2b43abc44cdb4aa8a0b0dc67c649a6c3b9cdcbea178401c0abcb272d8d5240a492956cdb2e4a3de78841ec42a7c9a5ab3b10d4264cf887b1c81451ebfb5e4277942e5736a7049bc071c168f11d6111ca6731a245c4f6f0aa9acba6d334d552193f8453f06e454c0f1b30e3264e919b2c667507684454b0045f26da17f27caf045c7b4d2f6be92bfa160277b4cf55287f67b9560a68e53fde3b7b85ee9043123cd7039658e7f874c136d1a2c26f2deaf00a930cd611ae4ca28ec30d65792e9493c2175e557b21e31a5102a8f42f94c87e31eb528d7a5fd4ca83ac3a1b62ed65113bf25e5e0a82dd2cae52cba89c170eaf2134fd4b818630ab330f68c040bca7285e619b2d493d713fbfe079f72b0b80fe7f56ea6eb5b20bf7990fa72a7f26191f159a2eb29d01801330d26bad38bdbd921c0e68d44a56c432329d5477f1e52f1d720e9dfb09f24fcc2f610e4303caced66f68ebcb8d876ec6f7c1904ab11800505a020eb41124069deb92d748417971506e406a675a65eebf63796897742b6080c037c097e742a446ed528783529668433efa5bade2e3f9052c11418f54b8bac5ae047f5fd30ba9e031f2bf38e36125c725b69365847ab2eaffb80c917ac7d3a97a47c1a1a9950db209d0326fe9bb980be5ef2bf21a3e85f236f53aa3843581e6e49d3b4451db82ff50218cdf7411b81f746a1086be6f27cffbf9f2352b4fca25777ae421cb673afe74d276522587e772895b53893a247b5bfb427dacfe13c2992700becb2313d3dc00f1876d430708da7298c907dff0b5984eafba5b26cd6fdfd59363b0bc511769da02db19b422e86e8e4003ad6554001fd10fd87d5f32af8a9d13f23bbe12525038a1a7a3867ea9741941c29e3aaa8491855617b16f140ded2b19a0b03cc4a4cd74c20f4111cdff9aaca53255e23c1c6412be63bccc4930ef87a63fb8e59411f56d4214b5fdb1a07fb3d5e91b25b3df5df862ad9cd72d3a24325c122801358aa149d2c03a7e24a687a2135762b2c1108cf1432e73f5ebb8e20a552ad92a91dbb185f11f9ede94d1ecebb9b30bc4c1a7744e2d121a63bb8fb54c2a83dcb8bb61cd6b600571edfb0c5ae31403c4fdebd9bfba727fe0422077b4bc1b660ad5ec5db84b7279d5845420a92bc08b2e5698b81383cc1490904ea5f20406d04b670ac20b49919e8beffaf81535d507d4ca8b08778bb8b791d016fb0c935e0ad7f1c6146cc4a262ce42077ad7d82e2e3848618c2e9514a3422581329e323cfb5e6153a6131990a4326ca63611c14ab69058cdfe7289463baabde69bfb64c895637ad70f47acb101d099896c7ba7b7aaeb2292c376efdfe9278aeabc23363ec0d2c0fad2cf14325801e832e9d2edeffbe63a053f8d6ac11a13118adbe3cb26f66bdc367bdf2a11bf5271e7b3828c52104b02b5f157695823763e7093e3e9e680921cd0c3dce450f4251845969a5c04cfa21f68c982ad2303130bdb22134d6210746c76817e25a249c64fff8b9d2e3651db73d13eea4360f99ebea5b17fd312fee8b1117e4967004fcf53c8939497fd46ee37dc67a58480fa1d8f2cba62cd1a04755796bdb1dbd0625d390458c05bc57aa6cec0ecf86e5b90d7d7484ce043ba332d42367e4b5310c45f065d59ed77cdf230d709944f6dab4c7782c82a2ce64161caa970c2c071c11e59f3300de1c8e44688c10c9214d8ee5d1402f31710c6c033adcef7c521b550cfbf8b66cb58eaf5b4f600593e7a7337c5b37df382e5df9bb7da69acd0935db2f71443d93c858a05143b20f06298edcc053d6f52755f8791f02c80856e1f55b0eab451543b5001697d60235605a12ca6377956f5161eabb574ea08f2741d0501dc63400f0e57099a68241c1d29056336dd70b0ed371da2615573480f10f933212ec6e0b1f25cbfe5ab5958a232a3c6555bc5d9a88ca2a5433eeb71500a178dc1003e570f070517a79e4ad9d72892bf563aebc21f65a15617fba60ab62749c622bb9822e4621ceeae8fc692ec1384c31b3fa7f85ea545106bbd4f0c8b5b03a6a0f64ca4fc38aaf821910592887b951fe39a88fed773f3c32e9d3afc97ee079d406d44e6772130b54178d8e84cf33390e8b880c554746bf355472185cbff9ba4c218facbf53bb5db501c6c583ac7df90901c1a4c7bfad4dc5a1f811986294c3e30b018f42f8d486433a79cebc05ab5a4861a0a19f413346be6108d93bbb7ab24d0f1e374662c55fbd4000b52009915ae41ace2fd2798513822da5c47b69c627522fa60c93b96255feeb8fead0646f6c03c55bdf0ce8c90d8a4ced28275e6005101dff152a15dedc7a5077b8a6a24fb6ba47511dcf942b097d341439041c53dd82030dff114cb5c1acff5f422b9f9157306540025097b5ca9666848657a1bdd8ac035532d8747b5182f12d2d76caa80b6ef5f2c23ed91c230c03f3c38b667fe41f03c5292df7fcc0485405a81a40666c864a1c0bd0f9797d7f00211a2a4d7073be1a8cc3df21420e1ac68e87a2eb75b32473cae6554a8c5118662b250f3c8e00ae2e2973ca4bf0d376bfeeaabf8f7b5c4eb4761a0b51fc018d7a205f02979d60c2027172019511cbaf761d504bfb9dfc8e01e45cd38b4e10103c13b2146174d8fb073f021f5d8fb8369b690f7a5df4044a2acc9e308e6165e6f7f5ea5d0d5f695a201986328ac16e02384a0f49de5929baa1956458ee19a901e8fbe31ecfa803bd2954fe80592a60132ae23e7c8d4d79dca4d60db2dbfe343b01cbab95a814deef0d2f09686caba0a5e8714860f809bff73ea57ed27ff26c921247633ff0f53d010898444c0522085bf95583c6bc1ea11245794f6442c79c3467afd6de0e938b9905fee8793edd702dfee158d66580b78e7124a0aea44968f9ded2dd0500d8263218735ffa49cc44890afd2777727aa4f9895a7039122d6e333b3520dde3f6096c29ec16859e98a504c88d771cc47aec7ff40a9e4f201ca83dfc7574e01261a9ea214114b3db1469a1cf55002bbd38d0ea429b1e24004d7677a57f16b4b0cbc25803108c1d04f5c0c3ef669689f62188b71909f1c11d95d514ca3a78edc8a7976422a8c196a42444ac4076ff06e369f839e38244111f04d4f74cc3ca2d635bc3001c60ee36b09471a5ed2d5db3a067183c50ddd40f2d74469507e949541d101d7e16c41645a3fc37a00b4708cb588611fe15d807d9861ea110e6883f4df31cc8ec278d447019f5d65f772020851c65d6a10ea601c7c4bcf78e58fc905b2c664f0d2c457a65f6424d6739ccaefc361f1e700d1735c61328bc29e75cb0d76b1c013910c82a1c8b34214adff97e9d94943f069509a46742bcd835da11ebb9b440dd3624c70366a5708a3a10b9688401e4250bf48266aa77d0ec6e5403d5db41f718f30f947098f457716160288bdb4721cea1adc69d2745d7aefc57eafbd2e901013b2ccf7ff182845d214e0fbe248a08364934d0ff522a9fd3005df960972ab255052930025839cab3bfbb81d5127ddf6fa01a61a613a6e8f128a85004743292f3cd2c1792d9ba72f886ed7a121297461743136ce348913ca557297019e2a3aec0a51922836c33d87bb152b06b9b9f3bbd144c703eb94cdf020568660d841a255e5d276bde9c1bf661ff908ef89874dde149fc4dceb010e3a6a382e292716cb4b8d916263774fee4ab4ba83a3ebf0b1064ebd75b9ae887644471f86a6bf8221107cc078182f2a821d52e0cfec07bc6657e50526ef9fd0048ec552d0808a742742aa625f90291e0ca4e5a4bd0c2588f4f2072da4cbc09e48791ccc16cea22ca882cd00d0381fb3133577f3d82a0a0bdcb0db0e88fe939063377ee25026eaabd38be1902111b320f710bb3503857b61c13fed36475db6ed1080063626c619d22f9b0932aa34375d85de0439ab7c194f504bc2523abd9627a8eb0c3ddfd751df66f5c95293d54b5e457bb7a9f8362a8f0e2a8acc4586c5f9109067fce81c82242a2eb5f2558d88d1bb575b0651061ce1f9bc8c75a95c5f785ef7933948632c1cb5b43592f88a62189b6ddad9274e56b188dcf13db455a4801c175f2bf802dd46ce5aae405421fc32aa47ef04f6c1ae84584ad7f02e065e6a7cfe8d1854a0f5431c7dadb0437a9c689c31a4cea1d48dc8bc6b635382ab3f7dcab912b6169fa9550900047047ef7b1b8393d511c82c6551d90fecf2662f336597b24113124e3d13b21b27b05f81ed1c2b356defe173c7cb7e4a5d33e4dda4cf7020e7fb85bf2893696a1a3238660d480aafbb0c4271397203e1a80122314f7b9ad807960830b0cacf78e1b1d45f76d3dce3e1db3071e9055eddca41a84da428fd19241adbc656e5cf7255b0cf5ad55a164b6805f639c3f7202975845aaa64ec03b57fa721daf278099b20c04630ee5a1d2cf8aa318da7097dfc780e843e1e2b8d7a550db5cad5d2ad4cfbf2e115682cc648ca983330ffb7283318b6311873be9977637d3acc6b2eb932bd00f387b1c9aed7bf928c15e9af4b7c165e4b59cc38d8e190b8b3a81d1c6d9f83725e0695fb46317bebde0aebedd01b2ed491ce402d501aefc8f4c1c293705be6b000354686877258d5ec4bec33fe416c64ce5925bc9d9a537b439250967bf458702d23d65decbeb6752b51e297d25b2432a6f5b64c0773b205c4e1446b087a44324cce538d0ae9959129c92b2240f863c773941aa0d1c503d20b91f430a467b4f2bbe0a36f9facb42624703f09f4288e1c7b74a9cbc5b2bdaf212a49f219b618c2580560061039f9eb6c52b7688ff624844a0a4acda0f1db128eef1bd0259ac3c176b1db37e377cb9d25a85c75ba3643e894de5bd3e998a4160837cd75f1cdc723038cec04a4853a6ba6c62cd4918f1632ac30de6ed5c0f8b0a68398ee741e60f184ea704315635056068c59f3feb2e28bba86ca05e77abe071bf4d179a0e9e2d0d2bcc51f8249afca59bf04cfb916937a0183917178db300a3d477e6c83f018f0db99b774c888c7019932c91882095d48b2c84a5004642e7ab200a22e90de77515c4e35e9d6ec6d71aba421ab36b30e7328fa15dd936f1c740838181ff349e122ffeb6ee1157415c7f104ac0f38ed2cfad62acb25a58a89eea6a5f66b4bf392520de5b7a89e66f24a828b7141b01fa5c1067327831d040d0e7a07f565528f2ef250121b6f9948bb2f3f2cb4099853f11073ea5173ed10bf2cd22f90108fc7c4c231b03d6f484e3bf256fd3d4f82388d90090764a9b6eaf6cb2cc2806c5fff469294e11a6fafce22457c6817315bc429ad19853b7957af2af6c3389e10c5a105a2e8d982fe73afb4addc3aa9b344fabc343c6438c8a59098f5936add0947217cc2ab165ac1555e0264108944578a3738595d56b7e274644af28a52c992a33209c05dfa9b8864f3155f77a4b18eb1eb793f4721180a337c7fb5a0b499ea96afd5d00044ba5da625f9a36bc38107cbedf12e0a51a8a2f59f2096e0d20e065018da92bc0a7112a7e48a4444a728d936338120a122db00a096cb486e3b67164120ed11205e12b3245fff17ccc63ec28b2e2a1f3d2bc7c8e7403e0c9c5d5a1514a2b900797eb3f3ea1748dfe9e23cad65e839d8e3074d0fd24a1bec13252b57f821f9d068c1cc3c26382875bc0d27da9d3bed3fdfed96ce2b7ddd3446e35d91b31d301164e3aaaaa7534f8ad4eb17b0621e6e5c876d84fa1937a023d2c29e37bb14c9202c71cc7c96f699ecaacae99aa36e15dd8055793bc6276958cbcad551d2448d1199499f2720dc367e892cbec806b3c9459f49f22dfa2b990fc8282a4ce499ba20799ba426355a8ef8dd0fd899bcfc06372d212c6ac1f63c8bd5e6914b18cbcb61704504f2e1e2ed6d86e2da36bbac86aeca425a6720cb4facfb7fd429b85ad57032d86246b281117624b1b92e7da1c0d27bbbff6a32918fc2f5945cd0b4a0ae312ce4799fc5d07c7180005bab2956d23025adfa9e3e37b7a6166084eaec7740518fa072d7ef56b4c1f8aa21142872240e2dbebec78db08384dac3cf7c36628851830f724379349291aedfc0f9cf05c92d4af5be0a50f586929489f98bc520eb906de5f1f41ebe26c58492fcf53552fa0d1df87ab01ecf27efd93652d85bf06180c0bd83850c4ddf904a68bb0f3a24d6703fbb5e7e3e3d58561e1f21783729acc1c3d2d9b96213fba91b08d5a06eca9adb713d1636c1c20ee1c6ff40a786694870e24656dcaccb56da1ff15761c4128774105f4869db44cd54a07414e76bae9ab2bd6f76e121840d8e8f87364deaa979ff118965f2ccb1ef97b3e45187af7eee1034b6d88504c0eb3f27f35f0f36cced25475b2f62df8cceedd3f4a9c7808fe41219bca2c439d0b29739bea7622a1d7c44d8a1de6d59c63b7ab55283f5df05cb70692e71c660ed45d49f1fb0790bdf274bb6cfadb9fe7d0c436f8f784478cc8df189e594ea67b7432713bf22c1199d90e0043695858aa46cccc397e528741c6b52e19c9ff45986020241cec343c8d8f74989c21f2b86e94a704335942c967bc9c032ae6b105131dbd3e550889cd96d3577f80542ab10e53487ebad39ca428ecbb236b4ac29357495fdbcd24b194b37aef7798539cb7407fc2d16cc02647616fda1bcd71bbaf8633ca0fcd9bb267a051933c14313c8a09c665da7ab26b142e9d811d717384396cecc500f378df1db9683fda74d5bc2c4e78fa1a89f7cf4975ad69066156264ceee9b8b8926b5a7105c90889998882ac9420f532112a6f1f2f2b9c023a3ed64af572f19d017aa3ef6afaab087d3bd768155bd3df0fc9f7f822ca88009d54f9ab76967e873ce437bd0f7e3195cb48599df46f54ef9d2e424a93f59213e02a423870816fe39839111cf60a4d2ec4acd8e43863cecead8f660361b8a903bb39e0d785dcdfdbe89b85c713b3355eee749fb6027ef71e9984d38806510f18a4b230924b0b848a291f8e62922077c1034005c5ee611f550a746bba75289e04ea00ca60efb7b8587281ce5223eaaf8a07771df77a7dfee0d61b4d1e01602517cdae4f6c4ad7205b5c2d781c36bb7f9c36d3a1db2c4eec78e001dc931594700617d097a075cf9b4e688aff67aeef2b5befdca4f21e388b8c655c2b55c24e511251c2d400d40a3946224c052f1ce75a4442306569e701bcc086439a62f9e04c0758fd7bda12e1ce40b209d30d67f2f003fea544afaa1d0420f6608116269508081983a5d8a574b216c19749a1472029898399220ee4a6506f95e7c5787f5b9925e200ee2c2cf08cf40b446659aa9b72d867e89de012b1336c390f8884c2087e09f19183af33cab63051be64938e007258d7bc38158dc0e80b4f2b76d13813ba2926c0c2da99395ffbf9e8cd65c95721cffa67de1b3aacb1e40f9a75ae46d9a11e06d148dfe7adc51debf3a5ed2958ac53f09e30b368648762c53d629ea154aa10cfdd10faa8de5faa738ed3d7bded2740c0187ffd6fb2f51ae27931572d853d27b75276de25a8deb23b075f1581f71d8b45f9525f54c924a3057476123f900f1f8fc748b50883c13789486e4a0750c4c2b32d747542761d6b41b5afdb17086b17b7f3f2125e5df91bd8ff04ec54b3b732214ab81bf6e21560db4780f1f0ce7329d6540a217743efe23cd1c6694b52623718e1478973ba5f76afd37d376b8930082c5adf2d53ad44dd629106b3c3b3ef24829f1570d6fe0e6bc673a33d3f233406fbe881c13361f5495c94f2d43887657f474a5ca6ebdad16ee92ebf3755c619252bb90cee7c61ef1b3729012c54101ff03af47b6444e81ace561d759d309b050a82f6e71cfd7854a68ef7452aa7e48f7c5ae0d48b5f2d54902687a8f0bb78852bbcb6b17cc2f8cac0ec1526e2a60015d3efd9a0426d82aeee4260b71813bde80392ede8d86bf0d16af0977963ce5a573f7d561fcd20ab05e2db8ea7ccb334ab0307670beb2978093fdf7001199dd0d72b0b7752ecfb91da30847c8243072a7c2f199d811b6a6c1635c6ec835b05fd77235cb3d8451194f699293e70133d756f2c9881849ce8eae584597a83c53a47450a578d88f75f6982d17ddea04e9b9ed5095939009049cfc7cd6cf9f412a3cde99720adb91ee077da2aeafbc7a033044a0b672f1ad786fbd543528c38703fb4a37c1db9a2674feabf00ff33a5ceb01bab1aa064e846cd5d748df1714875b6a62c9dfe562519ab164540c0d7530589c288064e7b496b7184387d1d4686c18f22110364c55e53f3a70d016832be570218a012f6b252d06f73d254937d9062a293346adc20823419633ffed17822bc3259dc061a1a8863d10da15f11186f71f04b20e0d152385bc108ff740a68bf824f890f05b422bc8f53d3255aff1e028a79f6da0e0e24e931497d03ca96b04e687a6f6300ef2bf5af84353c1e1a3aec79fe45be15ff7c3791aa7dbb919da84d1106515f2d5ecbc9c4928ef292fc4de434d4b92ac996d21ebf2d7d0b9d99a295622cf2ed2ef5cbf883993dd846bae8f4fdbae671e7338c6e1743fb26ecfd0e40e409c60b03445ba5b1bad93b6d8fd88b0dfb82ea5a9a01db23cf5f74ec68b4ea42a309c624208abd552ce7d9b316865cbfad7d0c232c8f3589cc58f93168ad5fef7c3843002e357ed1e5708133f433b4e0976a4b1d8f75d99556678833e17699e399f9970d0053375c14a0ab5cd8135fe265e537ae1d98e32f64258458b7ca404128db1e2c187a91f43be9f07bbdf161b002391f1b31d31a2c8f62d7dab2f8d7c4e618a71a943d0f30fbce765f0a81a0b86f000a9edca80be34d9fb22fd5959a52dff8b214d45dd690880833365f782eaa2bbdba3cc6758d345dc129399b5ad29e5d8b1f25d06a5eade8a0ef42d3759d662b6c3b27d9ca7f5804c2b3e92f1cbf128efb05252410af5cc57c287a8f8c6c3e7e83ac898b59003c3c39da20e238659b09f1df157216dafe77fb079590574e4b4123bf304f18960ac407b2405de4825cc00c35061d04805f2451cfe3e026bb617a8e284ae1bb816526a9c506a33186bdfacbd016102199b0ef23d253849057f884b691e22de105d33dd87777305d5ea3c564ff00af3dfa4db7f4b2aa305bda33a2476a6b05cce69d61d9f75f986dfc4527af3f19102cb5b69b2842d6d7e9e90e8d575adbc16ad8a3003209e911b2a11d4e200225ec90847799d4821848766737493f708ece7a692e4885692d011ceade273fc4041c5d41854376102a27aceaa0aaa4c3257eb59982599d817fb2605623931306009cb304bbce9135174946a02bb039b7995536a8c4dbb218259ddb2c1c5b9dd411f093e4e518d590f06f8c106fa0f4df4b40f10db27b63d53eb4decb2481d15a1b1f9a4dcf66168ae5f6d7c79354ec366e79b5875482aed5d9a686fdb62b17630dc56a8b87bc1dcb6329bf2d19a56110dcc82f939acb1a6e70e994bb94ce7ff31a3b1db0cf696f5c842eabdc0a6e9d5372ecb94426fc8faef668ab3d9576705e304a945cf241ce3038a11c6f5622a3422ca25d6d0040b99e5cc4e4b39843b7d72534843b4222447511a5174d7c860e10896b36b505a0e16e37cdf8ac5691664e1e5ea49b6f6f35384f56d9faa8b6aa800fd2444e741c31814b3bb2c8c6cdf828187c46187808eac8eed732b528bd82ac8f43e6db3dcd19ed12205172f578245a1589aff49acc083e14f1caa589eabd4d490f6940acbabb055c05319dcded45bf21f0d43708de5ca2383859008ca641deed357917f5ec7ba58101d82a5cc8a6f524f19284b599173697a880f26ba158d39dde2cf107c3dec0a9723aa76b6bad28134544a94eff974c94c041ba956834fb7b8a20cb3f9594b54e896704c00828401eb747c909f8e2fd22eab075a9078c84b13b6bbac635ae9abaadb151c6e827b606742f6ff2939ada26729759830191df4708924fea7ebfebcff8f927d22df05724b550de94617b485e6049826fe78c7dcef0a4b2d23fcfd60d744b89dd19180f1b06d00b6b4c79b71bd446267e49ceaf11951b2d9082b09d0e3489fe070a744a0e64fa7d24d7703e2e82a7f26381ca659b1f66255b7efd0df075b6ba4de7a085030738dcb43875953999c2f7d5392df24b864833ccc9bca50062a633f8e7b8150bb74b7a7c537b12c72b109f8c212bdc542940fcbe580040f301baf107ae48ae17da9f15e0d3439be0bd2a45788973bf8745d03428478327de779fb78c055f53138a8e8df3e15405872ddae7b1ce4515871a20b0787503e861c20abb64c517e0042a2680e7a04527301326182626e560ba34cbcd567a6fbb797077ae12ee618312c49cbfef179c6440930137664d6cc87902a61de833c0aed0a616790b8a9ff71c492e8944bc3f60fe2de37788fef01f939351f4b1fc27098590ebadd75746f60b012641c5ef934924a686fb002c329d97277967b2da3706e342c7418340cef72d0392b4fae0d4ac069893efedb6739f3bf6c4ccc98c201a908dd8f2c46ec94720c00cce419319dfc91c3b842b3e8591e284dca480d39268d31117cdd8efc4e520f283358f61913978ac3430c515e7517268d57226dfab2e09755fd0fbb8654723c336ff3932cd6cd57377fc84ad99e65e92e3e62a10cd8cb84d1eabe63874b70602ec92c43565c29787cfbf0eb1066ce59023d4ae6b066217bdc050dfad86e31c4e357566f631a0ec0d3fa61c328756f7e8e9d5b3b7c8f86cbc08daabc2e8f31bf8eb84ff8a4a6db3643f1495b21c73cad93e3e2fd220aaf09a29cf4c9c5864053a1331b441aa1c585625e656e3e58df740cf3d457c40b0429e10e67b7544310643409645badd52389b0023568f354686018d5cc617ca0be53ea64144ab52b5109a28e74e789766193593c8bad70d10b403c06dfd7ca3b97079f9a4974f563a19019c8c895f9372f50226c3b0976ba2dcba703af39b1c50c7a46b720ba7d6491ee9143c7b653930901b4617eac146fee04757443f36c7156af58c8bb33670bf2928276bea49e7849cc27cafdf183902bfbed16820c68c2238e877290b90a118068947b5a8977360e5a952e0036257dee5e0443433bc39b931f3a78a42a124210a51c58cfd80c56b67a52a3238b4a47524f9e269369e91ec03c1b7ec246c04420d0035d930d1a4936dae183715bee51356873764905f7e7ca33c3f31a56996e22caba046602b6b24130d134eccc1cfaa4eeb4a5eba6fa0cbc022057243d8d478035a50d38b9c15b98af87ef36ccfcb1a41265398b117976d06a7b604b0945f6724c34befafe430b6e687635eb05e98b27b9e57fc05ceac658bbf43be2b055ceb12385938b72f3e4e0ec92cbec13b6b816fa60b5404b418c33fe661142a616b6e24e8f9c3bb35d37e457d1dac5b9c74f471beaf7f8f4d26f608f520ccf0e113801460fb5f0d9b7c6ec1f81756e4486d34752010a0dfd19ff23b9bab9cdcfb1df1251615cb508e4922ecfab788decf45b7e877c73e46391f8e7368dbc5f351ebce144bd31203ce49b741425627c097ec248008f704ab6321f4c59e6e6c1adcf21d255b9c0abc5e5d1e913335b2f5c18fed758628396b06073450bdc8e51b9ac17a15988467eb7f3ec60395c779f28a2a1d9998f24515ca5b47eb5cc29f875c9d1a1165b3fa07fd3b99b18269fa72901035818a9bd02031ad308ff76ede118c97350024db3130d1c0b83bc7b65c3bb1f886c3996c4cd8939139aeb8e749c34ff131274570245f1e762ee28f9876c9e240e56dbafeda6fe7047f8f56148b0440f23f0d011a69b14d6dc35825881a3eba8c695d89526348a2e783135364552adcb2de2bd4bb42f48491930f07c20e89c9f87dc463d93e72a7162f518cab3474af3cd60888cd45bad31620195330ad1fa68c7b800b83761b351b440ee1542ebddff7fc14b018c913564c935e83398d0e28e5cbfd160707ca15b6e4efde70def302103524f2d9162a684c86e6dd17b74948e34b7523839b5557457dae8b84d9679010b82b4f9b6ccffc1777904eda4e4583e195476f62b6b2fc96fa400281d935327130216f637efce4bd7967925161f603ef63854cd24c1637de8a8fb2fcc7031bfba6108270c3dcf4e240a99c15115a88522efafbcb2905c304d35cc0db64799797a10e49bd25c35c762064bf13db5ed47baaf7dd7c0f4a4e768926eb2b5f9f2b7e9b0780258805e32665ef178dd88e89fbeb7c2dbdc7f4c7672d6cfa8a46075a304407aba54c51707cf93558bf6f2bc15e138ce94d38ef015c8abdb37dc627159b601389b40547fdd18c07caa2e8c156ac50719ee2e1e57d111bd79ae6d03a1297a40c36bfcb7110608d12d503545f26013b67f6883ade09afc1a9ec2287c5e0b4fc18961675c195b7025239a4f728b6354262a3b333a9f49a39f5f740652ce6fb271e03230bc5559de174f64dd0d90f02b5f2a2eaf004278215a9d856042f48cb3e2028a458428b511f716a00e9432a28ba15a6eb8cfd6a26c33641dcf788c1f6a71cfdd7f6043c8cf84ada544f719aad323ec1d2ee9f5a4888114e008cf4c467cd2b509709b141b7d822abeb2f1122d0735485f29454e0077ef21016ddcd68fbe9", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } -} diff --git a/circuits/benchmarks/results_insecure/report.md b/circuits/benchmarks/results_insecure/report.md deleted file mode 100644 index 38cac38db8..0000000000 --- a/circuits/benchmarks/results_insecure/report.md +++ /dev/null @@ -1,140 +0,0 @@ -# Enclave ZK Circuit Benchmarks - -**Generated:** 2026-05-21 08:52:11 UTC - -**Git Branch:** `feat/1525` -**Git Commit:** `a6455239f48858b46d3a55562def9147c130c18d` - -**Committee Size:** `H=3`, `N=3`, `T=1` - ---- - -## Protocol Summary - -### Circuit Benchmarks - -| Circuit | Constraints | Prove time (s) | Verify time (ms) | Proof size (KB) | -| -------------------- | ----------- | -------------- | ---------------- | --------------- | -| C0 | 6847 | 0.13 | 25.55 | 15.88 | -| C1 | 57818 | 0.35 | 26.34 | 15.88 | -| C2a | 142625 | 0.82 | 25.47 | 15.88 | -| C2b | 198355 | 0.91 | 26.32 | 15.88 | -| C3a | 132633 | 0.90 | 27.00 | 15.88 | -| C3b | 132633 | 0.90 | 27.00 | 15.88 | -| C4a | 92515 | 0.52 | 26.04 | 15.88 | -| C4b | 92515 | 0.52 | 26.04 | 15.88 | -| C5 | 151717 | 0.80 | 25.86 | 15.88 | -| user_data_encryption | 53732 | 0.33 | 34.30 | 15.88 | -| C6 | 86927 | 0.52 | 26.58 | 15.88 | -| C7 | 104273 | 0.56 | 28.38 | 15.88 | - -### Artifacts - -| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | -| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042430 | 176112 | 3218542 | -| Π_user | 15.88 KB | 0.12 KB | 2972893 | 170308 | 3143201 | -| Π_dec | 10.69 KB | 3.47 KB | 3553544 | 187152 | 3740696 | - -### Role / Phase / Activity - -| Role | Phase | Activity | Prove time | Proof size | Bandwidth | -| --------------- | ----- | -------------------------------- | ---------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation | 304.14 s | 127.00 KB | 128.19 KB | -| Aggregator | P2 | combine folds + C5 | 0.80 s | 10.69 KB | 11.16 KB | -| User | P3 | per user input | 0.66 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | 0.52 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | per computation output (C7+fold) | 79.88 s | 10.69 KB | 14.16 KB | - -## Integration test (`test_trbfv_actor`) - -### End-to-end phase timings (wall clock) - -| Phase | Duration (s) | -| ------------------------------------------- | ------------ | -| Starting trbfv actor test | 0.00 | -| Setup completed | 3.07 | -| Committee Setup Completed | 20.23 | -| Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 304.14 | -| E3Request -> PublicKeyAggregated | 306.70 | -| Application CT Gen | 0.31 | -| Running FHE Application | 0.00 | -| Ciphertext published -> PlaintextAggregated | 79.88 | -| Entire Test | 410.21 | - -### Thread pool (same process as integration test) - -| Setting | Value | -| ---------------------------- | ----- | -| Rayon threads | 13 | -| Max simultaneous Rayon tasks | 1 | -| Cores available | 14 | - -### CPU-bound operation timings (tracked in-process) - -| Name | Avg (s) | Runs | Total (s) | -| ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.11 | 3 | 0.33 | -| CalculateDecryptionShare | 0.61 | 3 | 1.83 | -| CalculateThresholdDecryption | 0.56 | 1 | 0.56 | -| GenEsiSss | 0.12 | 3 | 0.37 | -| GenPkShareAndSkSss | 0.23 | 3 | 0.68 | -| ZkDecryptedSharesAggregation | 8.50 | 1 | 8.50 | -| ZkDecryptionAggregation | 49.37 | 1 | 49.37 | -| ZkDkgAggregation | 21.12 | 1 | 21.12 | -| ZkDkgShareDecryption | 1.47 | 6 | 8.80 | -| ZkNodeDkgFold | 62.33 | 3 | 186.98 | -| ZkPkAggregation | 2.20 | 1 | 2.20 | -| ZkPkBfv | 0.34 | 3 | 1.01 | -| ZkPkGeneration | 1.35 | 3 | 4.05 | -| ZkShareComputation | 2.68 | 6 | 16.09 | -| ZkShareEncryption | 2.51 | 24 | 60.15 | -| ZkThresholdShareDecryption | 6.18 | 3 | 18.53 | -| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.30 | -| ZkVerifyShareProofs | 0.22 | 5 | 1.11 | - -Sum of tracked operation wall time: **381.99 s** (often much larger than end-to-end wall clock -because work runs in parallel). - -## Raw circuit benchmark JSON (Nargo) - -Source files for the **Circuit Benchmarks** table. Persist this directory with -`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without -re-running the integration test. - -| File | -| ----------------------------------------------------- | -| `dkg_e_sm_share_computation_default.json` | -| `dkg_pk_default.json` | -| `dkg_share_decryption_default.json` | -| `dkg_share_encryption_default.json` | -| `dkg_sk_share_computation_default.json` | -| `threshold_decrypted_shares_aggregation_default.json` | -| `threshold_pk_aggregation_default.json` | -| `threshold_pk_generation_default.json` | -| `threshold_share_decryption_default.json` | -| `threshold_user_data_encryption_ct0_default.json` | -| `threshold_user_data_encryption_ct1_default.json` | - -## System Information - -### Hardware - -- **CPU:** Apple M4 Pro -- **CPU Cores:** 14 -- **RAM:** 48.00 GB -- **OS:** Darwin -- **Architecture:** arm64 - -### Software - -- **Nargo Version:** nargo version = 1.0.0-beta.16 noirc version = - 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: - 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) -- **Barretenberg Version:** 3.0.0-nightly.20260102 - -## Notes - -- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is - effectively 0. diff --git a/circuits/benchmarks/results_insecure_agg/benchmark_run_meta.json b/circuits/benchmarks/results_insecure_agg/benchmark_run_meta.json new file mode 100644 index 0000000000..fb21f2bcbd --- /dev/null +++ b/circuits/benchmarks/results_insecure_agg/benchmark_run_meta.json @@ -0,0 +1,11 @@ +{ + "benchmark_mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "proof_aggregation": true, + "multithread_jobs": 13, + "verbose": true, + "nodes_spawned": 20, + "committee_size_n": 3, + "network_model": "in_process_bus", + "testmode_harness": true +} diff --git a/circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json b/circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json new file mode 100644 index 0000000000..7187133662 --- /dev/null +++ b/circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json @@ -0,0 +1,107 @@ +{ + "verify_gas": { + "dkg": 3125294, + "user": 2972881, + "dec": 3640985 + }, + "source": "folded_proof_export_plus_crisp_verify_test", + "artifact_sizes_bytes": { + "dkg": { + "proof": 10944, + "public_inputs": 480 + }, + "dec": { + "proof": 10944, + "public_inputs": 3552 + } + }, + "calldata_gas": { + "dkg": { + "proof": 169980, + "public_inputs": 6168, + "total": 176148 + }, + "dec": { + "proof": 169956, + "public_inputs": 17304, + "total": 187260 + } + }, + "integration_summary": { + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "bfv_preset": "InsecureThreshold512", + "lambda": 2, + "proof_aggregation_enabled": true, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": true, + "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, + "operation_timings": [ + { "name": "CalculateDecryptionKey", "avg_seconds": 0.004296347, "runs": 3, "total_seconds": 0.012889042 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.020525153, "runs": 3, "total_seconds": 0.061575459 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.019100917, "runs": 1, "total_seconds": 0.019100917 }, + { "name": "GenEsiSss", "avg_seconds": 0.006925194, "runs": 3, "total_seconds": 0.020775584 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.010452847, "runs": 3, "total_seconds": 0.031358542 }, + { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 8.270900388, "runs": 3, "total_seconds": 24.812701166 }, + { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 34.927441903, "runs": 3, "total_seconds": 104.782325709 }, + { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 7.542320708, "runs": 3, "total_seconds": 22.626962125 }, + { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 34.922820125, "runs": 3, "total_seconds": 104.768460375 }, + { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 7.895315874, "runs": 3, "total_seconds": 23.685947624 }, + { "name": "NodeDkgFold/node_fold", "avg_seconds": 18.310982819, "runs": 3, "total_seconds": 54.932948458 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 1.566955375, "runs": 1, "total_seconds": 1.566955375 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 47.874825458, "runs": 1, "total_seconds": 47.874825458 }, + { "name": "ZkDkgAggregation", "avg_seconds": 19.861393583, "runs": 1, "total_seconds": 19.861393583 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 1.358248069, "runs": 6, "total_seconds": 8.149488419 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 111.87172225, "runs": 3, "total_seconds": 335.61516675 }, + { "name": "ZkPkAggregation", "avg_seconds": 0.925223417, "runs": 1, "total_seconds": 0.925223417 }, + { "name": "ZkPkBfv", "avg_seconds": 0.218732388, "runs": 3, "total_seconds": 0.656197166 }, + { "name": "ZkPkGeneration", "avg_seconds": 3.515460263, "runs": 3, "total_seconds": 10.54638079 }, + { "name": "ZkShareComputation", "avg_seconds": 2.332301763, "runs": 6, "total_seconds": 13.993810582 }, + { "name": "ZkShareEncryption", "avg_seconds": 3.965180039, "runs": 24, "total_seconds": 95.164320958 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 3.440710889, "runs": 3, "total_seconds": 10.322132667 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.103301764, "runs": 3, "total_seconds": 0.309905292 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.285312216, "runs": 5, "total_seconds": 1.426561084 } + ], + "operation_timings_total_seconds": 882.167406542, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, + { "label": "Setup completed", "seconds": 2.7038335, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 20.174264042, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.003923875, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 131.619168, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 144.272573833, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 144.78216475, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 0.009741125, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.000051333, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 49.457601, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 53.35583, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 221.027911708, "metric": "wall_clock" } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x000000000000000000000000000000000000000000000002b30b0403aeee459600000000000000000000000000000000000000000000000db8eebcfc671fdeea00000000000000000000000000000000000000000000000af8f618d2adad1ebe0000000000000000000000000000000000000000000000000002c5040e9c20dc00000000000000000000000000000000000000000000000a2de63e4ca6eb0e210000000000000000000000000000000000000000000000021ae69d4a65c7a8e900000000000000000000000000000000000000000000000a005bc241e7d733b30000000000000000000000000000000000000000000000000002aa8c45dd85d4000000000000000000000000000000000000000000000008ff1dbd0f4b9b41c900000000000000000000000000000000000000000000000a46e1fc8b19f097560000000000000000000000000000000000000000000000075006eb06b9214632000000000000000000000000000000000000000000000000000093433350ca3600000000000000000000000000000000000000000000000c8e6e0c4281f705a50000000000000000000000000000000000000000000000043c924cc53c6b0e6b000000000000000000000000000000000000000000000009a11c5c7358610a3300000000000000000000000000000000000000000000000000001c36379475c900f5f028b1ca9b05f4fd64f348afe95c048291c2608303e91a5c888446ce265e275b7caf50609f9aed4ce3fdda4988dff1b9cfa755d22a06ac9bd70fe011311d3021f96a67be4ae494bd7f310637090a697383e48e1cfe83d199fea464581f5b26f5d27b00505d5850189be9bf43d8b14ee58b28a16c87b3bf9d5bd3dae341de20a977a63187eb07fb817d6f32bd7e353ea5f2e4563e05af30e41de8d76caf2a05fb7411e0736765ea872b88f93642e1a0a122c2beda192429812e49797d0d2b0b2d57c7f9a3d89feb36de6e8925068d4cf01c5631bba6ad66d9dfea89eef42507b82d0cdde68023f51cd886423293963e553af03761d6bdd7e82c5f3f70581726faac8f74102eb6129ddf5940c15a1f473a25099ce9aed7c8502f304535e73f02c89d7edbdc59430a931b60bf65dba6ff6c4568fcf3361abd1e9097cc3446930d256472545bebbdf0cd8202744ebee6b32aded93bbab7c8b07493ffe15d753c2e767d9b71f63f422e0d8397639315c74a3f459246674a141133d6068e91cd04236270fbc4be415c1b3fb0340ab79c0aebf168d36a35a77d26f5507817a381e82997d3d4e6bf47cf282de75db04e846b05a1171e1b27b3378df7f46c8a0d5f452ef0980db9a94784dc6878f8768320b7a5f1327d5a0398b9090cde4a0301320d2c66315a1209ffec932d6318d851ff138899b2fc2f20210b9ba6487e63272dc9292a55757e58a10eaf53bb32db4bd956cb6ea7db2f7d3d376ffde54a0c481c8e2b91e9e7d106a81edff57b8a49e69ba1cbce8d8ad7a768f51040cd0760adda1f1b057d97309b56258908e7d0b72e311d563d0f33a89b025e9b29dbae8430af6c1d5dc9be373e533150316757b50b3d7f39cb3fb983eec9d368f3997f12567c3516a615929225c27f1166fe0b8a910ade60dec004d39048fbb5e348d1d7b8f14e167cf8625eabdff7f7262e220e7cad7c3169f11ed47467b15790ad046ae3ea490d5aef76bcb164504f3b903fdde68d660bf30a4a023c0639d3b0df5986a898291ca9055a779c358e4526204e571e880645cb2634ddb604f97aab892dd8b2c61206a8ad742b79fdd1cf600bb1b100e239a25d525acfe6fb97a5c0a13dccd21c3e1be9395c5f4a8ce11c7c9adf0450a3dc13c753c8b0d37894fdb03746c10c45e92b11c11485fd5cca6eef8495c4fe4a8e89af5ce2321dacfb3de59466cdec2510156f196cfcdf53325e7070710c87d4451690eff29e4d32a8e34518657116d7ac1562534b4b1a9267d5813b831d136f643f2511cdf969567bb8528b3b509274e022485fa86f947eb6caddb7d6aa964114d188ffd141b79fde4bec77997e8de82e1a22e50c704d7e5fa81ad6470e6e610932071304bd9547f446e615d36f44ab582ea1f51c35052fc32ef3fd4004dc82b1bf9a39b670cbd3064b42858de6d62c0e202f260e6c01dc5da58f0f5ca5aadf80c9a9810e700b5342d312a883895208ed277759e4d19186345a6d59fff1e1b2df1b9ed20c49e08dba5d430c5c889d232c0dbec12765d1f90c1243fec4c3d67e1d37e80f7783ef9a1828baaceb6f59bad6211ce93b78dc48f06df07f27a2150b2b744cf2a5a06a0df954aaeec4161a424e2eb3a35a6b9f4e985ee41ca8ff60b38bfce07c646fcb9cd13c438c57afced26e28939f478f3c70bb968c92a82a5be84d9453b1121028cc087d1d072e7e6c7c0205fe83b1a01ceba1e0e90bcce21bc3dd3be897d503f20f49b07d818120c8883900aac83216fa6bf485c26476d03e6cad5643b6c5cf217f866e372b1ad25028ac239027d059b6b09551123523e2a5f42d33c4b330bf9b77e09785e6e48fa657361dc82de67104ad93500c9741939e8ba447f6dc1fc4191770b950772b9f1c82772b810282ebc72e0e003f822c1a213b3ce88f42565589de9c8a22cd5e3fbeda3c210803bc3b27db480707058c2df76c782e29c4ec3ed4258affc6790d85b95c0102af61b154df03951ef522e8c2e2d7c0e65d89bbbac33c8119dd9bf436def3cc2b3767985ed2bbab59f583232694b3a362918f9f72a99af2f0f5af2efcc7697c2d2fcaf7859f1a53585929269d86b3c8973039adcfbbc308fee491644c480a7c27dfa9fa5ef3bd09bffee4577c9bbc3690003ea1e3aad93e92fb3d3049d533190deaec9660872e7fcabfcddd95bc009081df6bff849124e9e9df109416fc751b0d2a98b66c8baa3500d82452f976c890f25ae6399064f155ecdb915f4d008b7a002f0bba3996e8fb749742a4b69120e029e62ad24d4f998cb586da076c916cb321f43d09bf02b75a56631867d73c77cdf9da4de3492a4b650d78b39b367dfa510fe1e9c7137dda94b17f265f6938deee104649c6163212b3f9be3a66234369c113a8e3a54aa3505c8bd668b9bb77dd5eec4e994e0024de8f3203d92f6f301e1223b57e5d4a69b68f6d9c57d3fd2dba27ca0bfb145ab0c29922b9a2b129f0e3f71fc82a5b8a4a881948245a642bc118320f72a2723d6a7a9ebb1bfe185529acce021f5fa2445c3744c8ac48355df7a41a8520486e45fce375c3c6fbd3b33bac3c2e5e29bc45ca7fd84f34893fc756f888c9db6e8d1b33d731af38f622c11c5e180602f9f504ce236d5dd7c1068c9f472f64905dde988f8e4ae7f199def886026f21843689e58f2f3dfe48f8f5a637b4a71284fc5706e649278419b7c8e538926409cfcd311cf3c556de1e275dbabc9e67658d311ba3cb5c17542c186a7da2ffd129684a2f9bdc4f2ebead34aa0cc6f94cbab3aabd66c8fd8bf65350be1ef08abc28a673680e3f8c7e41567d1ded0d1c173be5f3f0c7e080b2f1545630ef9644321ed718522ab1f7584f6a4ad83079823dbe5c55b19701cdf61208e783f6516be01412b7a8746a2c86c2cacdb91aa020b8d1db64dbc78c4889165e11b4aaf173202bc28bc70e00bc24bb8cbe3839f97793b66099528624e7d1c7fca00b911b707b19bc81d8401fa754ba2a05bc955437d15102dd440d1969450617d725d1c0baf51b3d8ba9c5943f28453d574f21fc2f9a08f9752aa61f9188a9cd8574593a82ac1794392b927399e2682bd77ca428316d1a2a77fd52d12be4d3f7fd57b9b33a68065b4112e464cbb09ae6d219c9acc029a295cd67fe60517b04596e84de3cbd1602082cab3609a950d6731f100664632ada637279bec7ead801d854a2b92a322b0f0f6f0554856fed457ba9fc25583f8f1ff8d03d55205a052c67db1a02282cc80559294b448cbc117fe65a32ae25d61584c1993159d4c2329f89293162f89ad02d91f842933045e44515de74b0f6da4625efb734d885a51865862a4c2d8104682c6d83cb7999c411b070ea3a5bc939cd54c26d43025654b66025c683116bdf06294b2eb9482a612e3d67a632d391609406b60edbc28b9c01c5d5b43796bcc3d007d36fb02537fcd6d3baa7bc9c3859846fce0d7b30d65860a5d8d27f6d2718ed15850e16cdfce67e1dc1d63b0fc45f1a17a0d9c7db71f689e3766e12786b8c2a2d60dc28eeadd07ade9fbf8447fdddc4f91d05b10d136058ad481f9b4a7e024a17029bad7d3a24366de116e8077da5b101c14da26867735d2524bcb9864778e02dd5dc33744ca7e50e87806b9d773d44dd60cac69135a5d29dcb4b93fe58551024ca2bae5c4c5f6682cb487546e40abe929304b714f50ea72c453cd38dd5e8692c2ae119ad1d9ef3b605e627d35720d26cde98e914c811cc457ae3cd5252b79e25893e23371561e89a731da3343d9ceeafb13593ccbf355ea34eba49f2e63c47289f58bd3e1fdacbb319183e85582f405592fe19656cd0a23a1cfe79d906f80d1efe414b0a22a209c332036c65cc9d46751c183d8618b839afa04eb21499cde40a98b16c72709e82be7312a6085b0563e940fc80e1cc10ad90615a040be766a204743f7867ec779c8a1d0d5fe3de9462f19bfdb3da14dad8768e04f13148df882eec36da9754f273e15636d808ea116523f0d567b5316bd1ebcf6c4de936d03908774b8f1d132154642b87c20ac9cd2de8088f1d0a55de6db45a75543d1adb2806b5975df484e4db990be106045d32642afbd7a492681e8681c84ec41bcadc6e1527f9ce6a7a670a20f260ae45d21aaaa489855dd747e5340f01a956152504a31ebc51009014c5b28e9e845c3a143d89302c81d5f94bd499250d48ddaf47fcec0afa7a61480263c5273b8acc667e784402be671586cfdb7b4db3f8a0f5e1fecd2aefbcf44e5f04a3617598869909c0aceaf0951635951ed4180a0a9b50cca1bb2b5b775f56a5b46a804330edd52fcbdf477485ad2f90e1aa0e3c6febb4f769eb0a4e62b793e1ac9676a35745509b1b9137cadb86a1fe28b0a00b1ede0523ef0229d5521820274634a635d398df9b85908f4ffd8533407e995995fb6a6f4f8bcf15aeacc9028dd5d83f265d2fe95d44afb3baa593f702b1937c3a27a40b6f02d7058a16f98da0cb85c8c6cc07d602960eb28aed5a6b5a8005363783c4c6ddb2010fd07d26daf701ec005d7765e9701cf8617925e4dc28744355b61d1fc2dffd272b74a06f3520227be984cbf13925584c110fd52798ddecd811ade62f107d939c1379e3ebcb8d71f09dfa48f3f2be201f2cefcc38345ae3ced3286cd06dec45e9115b345f3e78a3ee45db7f9170064864d5c09cb0fc3e8f5b7a763846c9b6a87d11798becb574a394dc37646282a7f7c7db3824cda09f9f8a5741937915979ac90b4f7b5de4d1eb4a3b7d6181a242ce8efe9e99513e83b53a66d91e85ac3412a40f2ed30dc99975a2a8aef2f11dcbb15473e41cbbc7bbb7adcfb89f3a9aa708170af029423d5e3e33e5bcd5bbc040ef8843868fc9243d05539ef570dbe1e10aef20fd382c33955f4bb9721ff64b91dc1ccb64d1c39f422bad3029fd747441ead524ce950b1a581caa7944b372cb78c0f9933a173143d3c99f2ef5203bbd71341e1b580d87c30fdf3e28038e52e088ee5d1729da0d34f16b6485002e49708b2af80a0440fec31f89fc13213af1d473835795cc991ac856248d6947c032bea69585093d3901d6393263c2e887bb58cf5f9e8dc4da7a20bca245b87607de9ea0620f096ccbcd54b6f3921fca613160e8e406d7331807a16d33650e418f91a75a1b49155d96ae9ab1de581aebaebcb2cbda8934bab4be6568e7c921e6ee1e9ce8ac5b280bf12e8b4976159f25c05e85fd4f48faa1e5042455da26784f0378846b00b1019ece0c5443a7276dce936601960c620c0f48dfdce3d7158691ade1f4c89e8e16e33e4c84777d3b261850f67e4cafd59caabfaea137826ce0b785a1efe2a1e308bf9cb95df87413d1bed427d514f8d2f1ed5215cc8cc01b960d2fe3466acb780ec253f4c5b3916132fce27c7d53c269e49e0d5f5cfc302c1815c83a39a842a4196b71ab05ffccdc4453eb6121cad50beebc9506f5e6e7a86325c089c1505a5d1565e4e200c0f947704862ea0bbb30d55ef3b0138243795391ea5660caa47102215bb22876b29def7ec75957f10b4173c7f04355665519b869e5d51c8f7e156b05af22ec3dffc3be18ba8bef4b6112006d557dbc866a984f59a6b4850790c67d09dbdd531ecb6baede53b5f37ec5bf114667e1d26ff037959d61dda4206f612f01df169922373268b1e7e6fad457ceafaf8c9c25e62c37454897d9732b6135712bee728c4eebc919211002d9c6f8735af7b8c0a544391a46f28c3a889d0f2e7a104554aca10104e8de7cff3649c20d0b1f54088096a7532b5019e476647fa05e2080741d0dbe713cbcaaa2afa2303255df9d8e0d391a6532b96bc449d704437222d0edd1fc730ed424ad8d4b5a39369538c7d586ba3e59184dd5f2902ed1146e1a01c7b01250ab358965ae82a0f4528fa5ada19814df0d73e4256eb85c0782e527f1f4d0221ad31b598531d3812ddcc1499f25e1501865e17df02e1b8cbc9909234c2802c41b8f3e59bdcaca7bca354ac8a06eaceca80e576276e089cb5f29e61b0d1e6d85b56c11df39d49c0836c0a3db33fd564f9b81b2dab176127f2020091e84be762866d62955eb34adfb2c036dfdc79652bd5168e1eccebbc106c6e0842a6ad0ccc37f360dc838e8fd4104c57d8d74b677ebea4c070a1d5e7a7fc219ad1169ddb19c6d2d9d077f9a31b480259d7c4dfe8e603ec1953634fc56a67550bf2f4c127151b656bd10e7db9c9b4e19e526d93e483d2beb6d6f95d5dd7dc4ef270edb572eadb4f1e9abd0639860e241ae44ee499db89e92a7971670be22941efb0fffb8af56e85dcb0a48c09ae10131bf2a64b0ecbebfc772259c580506db69ac2156fe9c7240f7f66d4714e18bb20c07f44dc0cd84f0d9b1aa509b66ea5e255e0648ed0a2962ede02665952151a54e8cea60f1ce897f00a3abdcd6fae82e74b6212fb5e7fae591d70df03de924b74918ada838ac568031697a44c273484fdd102cdfa567eeb6b89ef0d9fa657e8943950256b1e7053b0824e4bef3666272ac0314f463083e7bb01bf0a666e722b81cb549429070f0cb17f6cdbd435b5a9beb8a2e4d8baeeb9fc7abcf248cd7dc6a01075c51db5fa579aeeab43a582b47399ced1f5044f92a873e6418ba412cfa0a8a2db3314585b220ed790f35ea358f93a7630fa379be8dfc67d5eb98c3aab5c2f533d6750b19ea957acc6bf9e2796920007f14afd5d25310bf4bd31ffb348452c2d021e55f237a212ae30aee3f52d514f4520a9af18e31091497248753927f99eee28859559b268451ff74aba9de02ee6b4322d7570ff915b07f2cb9c60a3b39cd3b93d33e3e05f149db531e7da3325e7bbc1384880b0aae07a0d00123c89a8dee209bd57008f71bd1a6a5778e3dc257782b1f4eb8695e6c34c7d409d48f8dd796db2afc7267b6c14299be5aa8fc680abb4d14671db1342e867fff13ead25dfd0ec4deae57cb9c3dd91d62eb03725d734f841d9749ba103d39a29deea0e29fae6d44193eb8daeb4b06ceb487292619b51b15221ac506544d7bbbb235a8b1e025e6f95a55d4a9da6e2269cd0fb34f9990ebdb13cb608f09dfd348a611efeb3a20ba7d4911414ef238b948281e71b4aee677131b1bd90b800ded9e14d190e183fc69564ae4f8e218035a2ae98adc09e2dd8911283d549ab1fd630223ac9b69f13ebd7edc52739fa0d86ae7bc7edfa3923fb49f01b6905210ff375d48be2446ec8ad19fd201030784ae30ecda07d819104ee91e060649b3d0c9e9e2b984ff05a62d46c6e9bbdff96c343c82a6512f86de130e771a1a878e6a41a6ac0fc0788e6b068a9acdf76a9e550c760f1a7d2054f006a5c71ac65c82a640ecfdbd3853275aa0ad3206b715cc3ba60919d7a2d307a318d84729e3e17fc47339081b3733d471aa0c4a156a7b54b1b9285d483229a32bc6340e223d87b4069b04d0fbbc22d2a54ac37af4289e9138d2648099c28729bf009c2d28faf64c8a8042fffd8bb899554655cd463010c1dabe9bd2d7310594d704f1e10ece778e01fa0d14bc72a9095f381f288abef48d73e1c8f70323019bd3790cf90609f16cf84220713056f45f8783d40e5baad22fef47a7e37b46ff86b16a7050120dd8a0182fefb468bc5f043da2c84b3c3c1233743dc0d3f812011ae063c60c20907d674016e758411738d08cb7b38ec5cf2675460d6e4979a92f1a6b69a6d80f09ae9f8647a6057406542e4b5cad4d751ce4851fe2043791ccf35977c9694b27ad7a862ed2c107a8ccf062000dc496547dc6b4d6f3b0ed91a689dad868bb70009cf51ecd1b12f4f015bf9b58c6a3473e82736e632d1274d46e8fb9aadc92ab01e0c7c4c9d3ffe24d2a3ea98e88d74bca4b8fc56d7d94fab738c555f2cb4ab724de351101cb87df73b68d48f182811d7bd724087eb2d3bf773a92ae7fb3023d05f7d92e780bc1b98e1bcfb57bbed3078e50104684c04a0297a1f054585de92e285d2c460dd30427ebc79eb4e26c0479727e1884df19ef51e10dc42db993de31190d3d6c988c1c3dfa4e0105fddb7ac35563ecc72a4a196e035f91148ec48a4b119a13ca4a98e0f7a165f6547ee9750bef23cebfb0e79c139d02c6565e92464618471f2483429f5093edaca8b65ec2d966a66317e4fdfa8efb3df367f2511fd514737a1a937a481e7d8cc9a03c3a60841a4bf7f9754bf2fbbe5cb6164ab948c00b2e306a251f23b43d7d25e038afd9bb94e90efa334bea6c0c33b9e527b154f0271d9823269f3414e5b7274cc402fcf960867cc389df326a885484a21a1730d30e0d40999fc462c52b6a00344eb43a6e589008ec5dabc7d3b7eadc04cef996180edb9f5dd33c7c3ec5e30aa494b252658237e1f49445b383aa5221481bf279c824a408a0cba2b07ae7260ac3a8b16e1d481d8bf795611beb58cf4994317e4a8a1f43bc309f0b9a6938bec0f485cd9f959630aa5929c9a94e40f0fa18f3ef52d225323ffaecbab738f0a11859f83e7823fab2368e875eb74f7efdccd9d03906d70bc078f6741689487c98d8823c61dfe17c86e49cff25367cce5f92cbc602a350181120e505a5904a146484af259d3cc7407766ef0f9d0ed2e8c8eefb1455df430b15d7a20b2b8ce47711235c340fbc0f7cd120ab132ce965274b695e8384b0af2f74f1a79885f6871a7fa28a0bc7c26363be0e27b50ec7c8fb14c67e80e2b47118205e0e8472e0dbd61b66277d3439fac17951af5ca2d6b5af0d524ccba21b6b2a6051dd94a1b8d7e7704680ddef09a1b1c4419f2a9ec9820da589e9b20a70ab24d89962987ddadaf53f6605cd3710fa08fd60f3daab58c4ebee1ff8f0c7b88616302942ef09f82722e003f1421f3722d0df287cad3f920171162ba91a98d6032d74219ce8c4fe3c7e33b0ffa14c83434d58870d116e6c36559a0c5276bd3ede2f133f4b9b09ef25e76cc4ec02d3a62df652cd9034f372c30b66891e912e1bef1e17181b25d1ba2eacf2827897b7b2d0c5995ca072785082d059c8644febf9771752e9b58e4c98cfc60c257c9111c8b2bf6734c7ac89d2bf8276d6eb14a2be82084e6fdf8ee543c1a3922df2d173bb2d7a7124e55a2d39ea70742821f08726c31b6c80e32786142546b5c35a2a9a2441745ce704f0e55134024b64036bd28f8d2b8119e8dd0e5cb9245d8c9058a66731ae81cdac1c54debe97ba687b26ff00671d6b69bb7f1dd005c20971d3f86f8949ae90483fc8972830abc6481f17f90547263749df4c5def86d1923157f67fa1738bef66ca4fa004e6fc5b4af721837799245cf824a7f53cd08d243f550311fd4a3d959daac4cfc85f32604bb35af71ca004ed6051ffde6dc1b4fe2eba6d82661319e9cd3b855ae418d73cbb37864fe3c10c42f2fc3567df4971fe7a1774e33b8a72c9ff4a7c9c666df10ab3fc86bdd66d2337af6fe5478206b02c5f1709d87b9df0b8fae401bfcd98b33c30e01e93675f1f355765c7361b84de13e9aef9e4fe7aad73a2117c05f9714a0d361e693470c80506dc72a579afc28392a084af4bb406e51d53782e6ca9ced5f2733ad62194041187cf6819f495e3f36941d42ff64e68b479b4c98047b50d14e9d906b8970b9a1a85df2ebcd9f91bd9f9e23d6a103a4398801cf9fd35ffe19b1d22551a09c77e050ac86b551234e153b9b1f200e5e4d128ada63380f68aaa80df274d0bd10de6129ec811001ddfa3cda1b108abf1a10645def9a8f6089074ff283c624782d8e507e2120f8237004abe881653eb8da68e34bd3aeb10e4a19a8fe7684dcd8fb2160e98913519c62998314bbad28005c4a83290a62f9334bd58031fd66bb0db262019b4cd0c0423184d42dd0524ddca8871512144dcffcd066ff6ebf9298ea84e812ae69c3c6009d6328449e1f9eeea8fecb6b9e5c96a196891777b729dbcd009b11dd6c86933cad747984c5842202dacdb14319ca09f16950b4ffd8e8380fcfa9223d511d82ba615fd3506e86326f1cda9dcc1cfd9e6d5f1ecefe79eec809734300c4b915bd8b2f50d9ed1228836ea07a31376c0e1f4bfd053de47bc04acebc1fe0930c6a3476edb691858be52b6de2b01d9e1e4abfbf3d3c5d2684b4bfb7c657919035941a2aae3ad42a9e63465e499c16d2dfab35f248622e7e58e84bb84b71d01e032044b717cab2d569ebdfe7d5097752cba06245ffc7cad1b18d79eb62ba3118c1524d59d9cefeeac71e447831f83c6f290ca10481e6916ae350b88d60f3e2f0058bdb51489572e95e31fd84ca7260abc39f36938b4f87e6d4835f1ad5fb221a652e8262f018bfa3856a8cbd34144e6976d45a453f3ec06fd6e376dc4cee60c6500c3280e6ab4927048494018c8afd9bc6fbdcecff3018ccf7d03b51a6eae14b7f8520c39787f70776235f01793d7e7f745848413062433200ef7f8cefee602784ed98b2c9d273cdecb02ddfa18a8a661d741f3bf87647e3b8d25eba2360217f3b3108225afa2797868ceaaa866d663881a0008e416c5f2f80af7149f93050203465a7c2fbfb1ae9517fbaca023c9862be40b2c84f30b81d237bd7c3798c30891c8ac7a91dc7b0b69ed9e0982acb177108b867adebdf348d894dd510a0b7e1cd010d9147a550c8f12f60691d111628d9182c83ce9536af7ead503f2d86b1b07c40fabfd7c29d47e4f8720b7806cee007281fecdf927c5a8090fe200db873a0142f5b631901ff537e84ad96b1796bb469b1b11cbe42c2610dd49aa74187a4129a1854db8be04d0079d6f88c31d140eed4d5a1139b6cf42a9b792ae577cfc7a2879bf32ae3070900028e0e30c27d2c5d1e44d731bd3d2e6d6135dfcfb18572d107551dbd61f608dff663d88629c18be0406c8ba65f8ce3422d544efd0e1e9000dcdc683eb0eece6d0ba89d46022e636bc1ca13b2918d04900ec63e29f630e7b09d795652cbd6a6479cde8cdeedcac973f5d240002f8f6df8a75ecb9e1c6d2b609938753bf2c7da02deb6f5f3bccce5e9ca2afebf91feaf1265aabdbe2ad99212954377b6f55c3faa8c03088b93f943eb08faff93d5c958f119889f11fd09ff304933fda02023e503321f3311c3248fc12cfcd7e748e21b1002a39c02c01d09209a0ce90d8026f074000e455d119e776d349ceb3da94cf523de5dbcff0f48b0c1a8048441e7c85fcfbc208ede9faedcd17cc3feecdee4c70aa042c6e441b059a14cf0077b5c8565e3c03b57181839f9f7ec98fb603c272189a6ff3a1d3a4ab621ee17eecc490d3d3fe3b69830871d0f9879d3049ba97804e641d42ee31522ecf1859fd0e2feeff6c4515dec6601e327c6ffebc6958cdf901c0ca743f82214679087d9626e792e9ee6cd489e79bb805d2da252d55a99150fe19bfb245a1fe42170dbbb9e93f584301289b415aa57fa49a31d5a2373d55a75df54190b1e216324d0c11ef5e89f61d21a42d90763ef5a89092a10e1acfb7e523d7f3fe9f8efbeee02c371b165637262eed95245171b22f53990e63e5d7277c2d73a4c391af7f2e4e1c18101c45f8cae2939e17c2a4999e38be4e8683139cdd427060e6f476809b670f35b2c56491d7f825eba12d6626da300fd3c80da328f0ea49b767a41a88ab8321c15d42c0af8fadf6438d42bf60c3a2b3adc5871de99e21556ddba86feeeb79168a27ba4c7f46b716afd29c915ece35e192e47cdff65cea5c35dd963a7b9ba5232bfcd3e8240bf1278b36d6aa6dcf916c22e853761e388d3f1a40a433aa34b506bff60e623daf6dee09ccd69d2781bc4d993565b6370e7684d7aaeeb2b1fca601357cfc35f67331a5714552e9eb3dd6fbf2119bbbca64ea76c836020d19ec9a1f48e6e38d20777c1c4cb8531ba222faec847efc90b465e9c34a7001ae1aae1a1b5e613f47a7d2a35f2fcaeb1539fa3306807755ed92968758d518fb4475f80a0a3ca9d6a6143384cc6083082d311bc387ed1b500266acb41b8126dfd9c93e1b1fbc2e1f174be1d7761604d9964bf196b9ee0a8706f863e1822950769d6662051d0bfd2686444c417b5ceea329e2584442c5bb2f75f8e7c180fedc7a3a6dc21014bdfe5ba28573f6415f77fa9325bba1727fa321aef3c424a762f29b3bf455f51babf7c357a4e5ed8cff49b4eafd2746f73fa06b1d5ed4bd15999b64d6f3a7121d69a9229292044de7007ec9f717fdf09dbf3b367ef4a7a1baa46b10b4e16ea903e4edaa9011039efcee40df9f889a16f1d4f4a9a29abd4c5d587197dc1da1d8130ef6d7f55840a3d60e13734ce26b321d00aaab7088ae1779d826e99d92f54b0b80606aa57ab42c90e751dc5b716baff6a81bf135f4250f00aaa0df2c833761247a34288a2ea47bb0bc836a6fef419c37495bec860fb6529465724011b4e58017dd1415346d52b19101d5eab12b19a0e441575d47307cd7dece7b39f36fa26a012112fcc778b55fbd14f16cdbd3acf9a9169d544f15345cb6173467874f657f11a12cb8d9cd9c39420f84a35791fac6511b78d593d42fc2b8f5d872900954a406331151227f3b4ab61468947e78586055c0c09c0b98362664e5e1c8975eeaf02cbe3fa2404ac419609ca705c7393296997f81a9c8423adfd4de864c57a97922159f4fd88748365f7c0d07563e4d453417656c8833e29d6390b86fe4b794250a151fa247a5e1b9c58149170293ce42a65b9cdd8b38d9ce0ca032a2513a10b9aa13bd582849621d7b26e71960d6ab34544826f40f4772edbf65887cf9c90675a02a24cd768068be5feb2b8020cab1f939df742e78b85890a0ed8735180b8dac5a2cb32325a25e5a55c3dccdd36db1e560cf06d8edaff94c8f8793df454115ab6907e1433a2fb8a9710e410cb4ae29b749c5a56812e5d7f5071e3f281e22439f66040be430da6e3d00d79a926e6d938d9e102c8de762f46132cb877c9cd0719ef3124a78afd322abf925c981c80564d0990d6a8ce664e2e17c87b4a0f4d853162214634684c7c01135a65a96a2dd46dce177320faba9c4559efd833f3dd5086ba8112b30bbf4ac3c13f437f5c875730809567be74cf8a9c816f4fd6716a9b82a1c0305ed2a51e4878eac2537a240468d0bca71bb3db3dc44587370c898536d53d80368b46b639edce91842b66e208b95b7592b85d9d72657432787fe6037eb85211c1c8adab3ff2f751ea8fbe3a00085f306569ec17b74af594427ec4560689fb70b4e6ed89f6eb53a9e4512918249b9551e0ae6c9f36ea559e9e512e2689d090a17415450b584e5c7cca50b33fac3b1787034d0208acdad636fc3d616d98f1dda10eb9991d957f037ea435e3319c366ef658c47fba775755c948610e7a384fdb026d34b79b7c65045b283b693ec39a3a461580d5e888e2bac27ea2c619e06650b23ef7817b83eebcdf4c1046def9755d8aae19209f9ab2556c7c1836228399dbd063a417e004937c4e8f1238d59628623dfef098ace7e0e94d254f75cf60370991373e1aaacd754e553d42045943f1356cd63e1f3db516c660ea72208a93925c913478d2a2d2d9e8f1fadaae136ac42187cbeaf41a1fecca67b1d9f8e45b8a21509b73b3cda6895c83a1346812b5ffe2c1a73db6cbccc95409b09f062caa7414f2a7b9182faddea8857099a424d6bff704d75eeb4793e53fd6c6b392efd365d02217bb0a66eb65c8ea113ee6800c30e14140d1db32a753de462a44320d257b6c021f6b2dd25f7507d6320a391d0b0de1d495960f32f421d11d91c7ea58f1838f225e75d81035ec78aae5007a650551a2130d8380a88a42cc5f00243adb65118f62bae9123494c72cc94cd04a9a48393508a4042ff5d4bc0de75cf5827c32aece92426e6a4df489f63ffd696a5e09c4569596d59d11853d305eb433044f7c58f0208708aff7ec7dfba646904fab95e07c593ad93e76728526f8818fd7aa58402a80feee7eb15a2f0b3c4cdb007544c53de0e1f82c11bd9d37ec0647180f05a657c2933fc48c254c582ab7479a9c83bd7c4f8eb0e29efa3ed8c4095fe08a1b447db0a0b2c4929f11cc7d7829662940098723811cecc8af257c9ca591eff5e55f15c1ea2954f52b5693fe6643d48e9f2f23f2143777b0cd7c33e824b1df67327692829046a46469f24a35f7a4a268c8071d3b08d4de93efe35e71b2ae80a054ae6cd0345a6af2d87b666f3f5babd0cd2158f9f1d0454c258d701393f9c43f96de381042f38b49a73be70882662b7070df771bc438452d607e3b501f82092cacd524912d368f485ca72dcc9607f4b1db83a3bd6b6e02d235791457fb5526e68a78d321645077c96714c920ce0f10bfbe8a0fd9da0b9366ee0db2b686811ff10cb381d13e0536e73e9b49c2341023a2ceb8651403207aba6394c9fe86be75b1f3c92342221fafa5b188657ec49279ea611926609c5a0a8b137d58691968eec74595db30a5eab47f49335e4f7b0e1c82e6cff21317280e2cdc9c35446b64b3c2295b7ec14cda916f25e16626a75feeb120e95eebd9754a395186204d03d9241a401df77194d9938769c163b7eda901948cbeecdf3e841abf27b3b8c92903ee36dcc5d060953aff4f2442313a8191adb4c9b374dd9cc5f08e8ba058b5c03dd111f489e461c1723525286162fc0c4846dbc36eac9cb8d32c1a121ebf3d81ccb322277755a2e55761686f53936d703806a52065735bcf568ac9d9adf605be45a56b980d198260445b04842fa9fd7ff6d367f50ed8553de4d1743d992577279a8d76586f479110cf8e5875e0c4682626f6a1e55d9116fcc4bebe725bf5ff24704153d8d7b94", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c03d33039d45432d4999b9964018d8d6552808849c83503f50a70d594c01b441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000c6895e67e144556602cbe13943c8169600000000000000000000000000000000808b3aef60f3e74a629e9331d68a14b202c2cf87674c88d6103108c767ab2ac48e35bee25bf5e92c02761b47af09b71a2c17de150116bdca2a3edbbac4f77678c67c35f056f9e969387b99b5c26da55509a23e762ede2ab19d289955f6fc34a289c4196b6f4fed818bca760158098e851aefe0609e27b1f5e4ba46cd640bb7bbcf5d11024b93b2b80830d9827e59538f1b1e385ae72fb3add3fe838e63d0e080c2409bd2fe5f57f926a01a4e31278a1e0fd8fef8ee3dacae5dd4bd8666f29088e7e2c1680f50451cf263d3e53ba2b8b4135a40b6144087f868125f387412d514b5747bfc6d2fe2bf59b31a94cba4d7b722725d5e5594a87fd3ee32e41daddbf89c75efb45a474670af2162a31702ad09" + }, + "decryption_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000a6a7b38193b9791f900000000000000000000000000000000000000000000000938adfed18caae40000000000000000000000000000000000000000000000000e21f5e071314e88420000000000000000000000000000000000000000000000000001dcc3d8161f47000000000000000000000000000000000000000000000002e46c7ed7ae9e45950000000000000000000000000000000000000000000000006b452c19c9c3a95200000000000000000000000000000000000000000000000f937207f5e9d7f27200000000000000000000000000000000000000000000000000011234ce13989700000000000000000000000000000000000000000000000bc917c80a1df8a9de000000000000000000000000000000000000000000000004158d1dc6c5fe0793000000000000000000000000000000000000000000000004df57309713331b9200000000000000000000000000000000000000000000000000007698a06035d700000000000000000000000000000000000000000000000b5363c615bf3049790000000000000000000000000000000000000000000000084836ce0c2626f54700000000000000000000000000000000000000000000000de7f5f0f37ffeb22c0000000000000000000000000000000000000000000000000001bca3c66a8927213f200493e130f2f241df6f50e717977080ee976b404ea52ce9a1739a6538942186c4ce5d408bc4469b54741acb17f81532bb104e167f13788ca258657e19ad18bdeaeabf611459f9516a80aa25dae22a716359feb8721e6f73ce82c65ed90504097a64060c9b98a318d0db5087034f8fb8be44fa54632799b74281b02fa3201b5874dca01aa6c15157bbcebfe698062bc743018cbf4c1838fd88dbc7725f1e2cc9baf93dc954f7630b60d425d36ef81eb1be7d8436127e0444eeea525630a31058031e3a95f311de89e96eed8d8edd125cde39fe180ff58ece8d79e1d4b19f2d6403f1677a3e0f98f7d8669a44ee9d1c5227bc6dc8f7fab18b5ea38a23a8a909d6b647f178c12bf5765e4f3fd9e9353f1578914f6289ab3626968195e2204a0b75d68cafcb752224084dafa2b4ac94248907a3cf380066780a921c968cac8c1745343af4f66eb1673679b9c00300c26624904c97f542932abd632e9d9c20b613f65677439219bc9826056b26cd285c1f6ad8cf48940c417ae82c3b6dda3ef601d957cb411fdb0e5a236ac0758e0f106e5c2aea4d997e4e082051f36f37a07e193e06fcb0040e75764cfe7d7b19379e865f44cde8c4021fe7a2cecf7276e482125b04ce7829f541ae0f786317799e17a5d19b18c35032546d862d824f01aa2b2ec87c35f2af31bba08cf65076c41c5b2a532fce570414bfc6619bde14f1f90519c766248124f4e391908fe40e4f4c2e47d959872db00ccc6c66104be6e3a4a01509488b91bdae4548075814bb3cda9893c4acada580aa2c29959515a57de7a31cc4f2c4335d29fdf50154c3993f6ae9031d06f0541f0e718afaf53d83631efd232f3df1898a0799134d1f263b0bfd6dccf19c72088d8811c5e361ba2efb54be181628501e2f6edc49dd9c2140408a2fa36d107456bc9ca11d66c1e426b5c3300cd87deaf468a61d8cd99c96247bf074714476fdffb493aaaf976d4dd235e99b154e79b2ff5e50ab7f8c6ed0796649601b9c5b5efbb5fc5f5b4f216469ba83161e04612689209e87667d0a819663d69a261da2a789ec0318543355b471178ee51394b1536f2652a7fd5ae364f01c53350a79a0a2c030636f3c2b0b1a4f11f8f70b7dd2a2ab687739334dfa9d83fc290414ae7a1369b7b416834d3971e6e4d10c1e739ee4f5603547ba8ba72746f349ac646d2ab09e4faa5ab7e68fd55609c6fe055052cb8a99d490c94cdd0e20fda8125a7fc867867440b977632d440bc72ddf2a79774bb4346a8defd31709e0d3be983bce16ec2f1b6eb0d064df61d33fe4eb0c0a9a31f221442947a6eb27a710db271537ecb73fcaa4ddc4435b85207362ff13ebd5a7cea692e2c11170b8f0e19c3996c9d4fe03f579bee10705ba2cb6a64120e3ec3fd8a3623c614a7233e19fd586b3dcddb5ed038a7b87085bc0d01f35ac0a910d04789ce430f092f249b8a95e3f3e9e57d464b7e39b11b0b2ce0511a53f06732a3e7d8ef4a98f1c0197fe86514ff68a832af68a50812a33c5b1b3d7357f2f0fcac439af16b2c3c51ccc318db8bfedef50919ca1df0ecef4cc3a1295d63a16e24b4427e5397d2aadf907e4021dc59573232a6851a4772bd0d7a9511e5e940c45c13faf58cea6eeddbb0a5bab2b269c8d205cd66a41192cb1a02c691451c60cd43ed39782c235f5b7222db00e52277c1bde022d68a3dbef6836899945390002967f653a03db955a5c8ce104f9bded599366c20d9e4e10815d1676c7de75591876c3b768ca0c50d93bbf03fbebd73298b550e6731cc9df74ce6b043d391fe501dcac5bfba80aea4039869994a026ae689f9fd7bbc45b9da35927b07a1400b8050faed5f52a92daf28e2f7a1e113694c5751fa2819d425c7038381219f561d31ab37ed6c11585362715d889de16fa9c110bafe87a89fb3437d64c75602257e3111a8aae71085083c00061af68a50a444442c811ca0702b1ba59efa80707a12e0574243f7c35ad11d0499bf94e73ddb1a0551fd5dc7137b31c2cb4ebc6845b5c11c03ef720b358f0b3417c3073e3d486c03aebb7b020db1ea2ebd23e08aa22bd2657df69bdd20d3023feef2bd7b0efde8763523aae1b31d1bebc86a1577122722af7b9eb04d62bb5d2dd340cb78eaedc412be64449d0314cfaf30771d59b4913156f4b94b14bf9ce19924e16e516ff1bc66e6955224645525d4eb36650422744014aeed42b2f48def893f4b59685a7a00fd42ae1856463b2ee65aa8f09c21def2f6d4a920159b6487b8928519e70a5adaf40e615c04e7b549c276109ed3888e808c5f4a847d257ea6478a544aab60e354c2cd9251e4456ba225db340d3c35b841ab4876ba9361592e0e2f351f1cef7a81e5875c24f4deb7bd8720b702b4d02941825cdc9b2fd2f8aa1e13b760f9e5f6b87dc45dd74defd1cb013a8894bfd211818a2926b858bb7f334bded04473e5c398fe186bbfa744b7c667066c72db7d9a32d8649606e140be47dbd386381b1fbb1a6238a8c38d9346a624d27d057ca8ef71e68e91fdcd8345fca7dfa89faeebce29c454669b769baaaba0fc712dc541ef8280fe2e79458e28de1f0312d7a3dde0572fe68bbf86f046faa8c2e07927b080f28ec7285880a3c089913308ed80d9c983352b34317079dc36f024c88cbe698f1199da4d50f7601e1a92ce7e958b907c3d08ec1c966d10f838864b06cb903a26f183dff4905cb4bd6861c48fc2e538b3fa22385197567809a7167ec25251e71811b35520a843903bea42f31b157ee343aa5335adec9bcd3120a6d1e4edb158539292537016521f1e1719774bc3472bf8cea73aec615aac019b47e7d879642f462263b44a7a876820f0117f968f8b0bcd1478699159b190360eb331181a2c04be40bb9562ccf1693476d7beea398beed58780d39123f31c1c401b3e59502899dcb070a2498fdee2da8d6266a91ed4ff61d016eaac361e643de776750e0b5e0675313820988967945cf62b69b46bb5aa6914de5556c053dd102f5065ef07c881fcc2858d7069c1c6f7915d99118c1d498f3232b2fbe53f19ae68faf49837c761efd1110cb2769cf48c492062503b64b03bb339905c4871ba162089ecb07e88f922b23a72ef676abd989368ca5ca6efaca4a3546caffe57f71aa15550539f41efe8523d17d0a9e67ea0a1f0e79adda4f7729011eeba0d13f56e2e03db3aa4ea0f1b62ac55a22371b0798281faa7e0ebffa3f57ef9db292b0a30d64528ff64c693dda2a3524a0f84f2bb33871e969321e34c0af763e3076e7f460e92e6cc346eb967e087fe895a6acbbca4f7dd6b474ce3421a5c24f07129f17b389059945b911b318113acae2204d6152a672ffe6be7699486fb5feb674a9fa1d63454eb801e0b0071570f519abe7972bf439e8cf28cce87ba571e359329415b61dfd9605d2e5ee440603623c89bda7c629b4a2dba72f964bf3fb0a45d364198059f2bd2b10ffa4bb0a422889998c100754be16415e5a16450ec06bad480226610e78379eb5be4c2f04a91545a58c5916584ce39798d9f1dcf7742481e3fc90226b8a508e45a3179b207ca949a9e68369a4e7a0e259d4706e083f51f4172b24d00940a5e40c5041721c4e2ba1ee11f1bcf9c2ba0876dcd5bc3b74e779fe4fb8318c7406d47ba5b7da0d76ef4979ba783d83ebf8c7d834f6b0e244320263afd46395579b9a73d00a400646144e651a725f8d554dd3612862c70c98fac4a6dc4e7648ae1c99498221430bb83c7a9e1667315b09ff19d292693ca49c81463918d0c8f89e56e7f1cb588f12100b03aae55f7fbf686c6d8fcdfd2ec95fbde59c33ef6e9162c39088034e3427bfb892d72474b03a197db7242e68fce96b631564d2f94eed762c1c722e38040e22909f91577a6ec1e2c330e5a47c8bb5e6ef018f1a83388c73eb754ff32cfb0c4d38ba8b27885ddadf3f520c796d6fc499fcbc2a3c5a83b97cb77a5174d60d2c1234826405656d94521ee61b88a233af7754bd82be24ee44bef1648438d4fe075b2767664640965dd21e2f989bbf4f2f69c5b1d0d1ca61c90f1ccf62a2f766251a5ee4b01447113c913372d0d49d6415b7a479dc7ef064c184b9140d8bee6a0c1089dd993d092a9e654da9b9d13748d9ff552c1d284a9543151223275818ac1bd3d204262544f9b536b40ac64089e0ffe99370db2f5a5303b14f7f1b43feb901c4dbe086dbbdb2f83982b5a415dbd75c8eb212760574f15b9065a39654e17829f0ed29bce220f0eb2ba77754697a8babd74c5dc104896f4bef310d99a77532076a118a429672a66d8dd32e72a259623f4a330291c23a0b39b4376abd57c6b92ef5e2faffdcb6fdf0419185e0528d35366a1b865dc91dc4a1e228379b219eb52a47d3160a98e6bef560957039f8687bff28bbd1654b0e1a2290f335c53f0e0016dcb8f0a0c2e0d7bbccd7415da0fd8725055b26393b87f071918eda8414cbfc13d7d1c5beffe174876dd3ddae96fa6cdf87acad3ba1709dea8c80f4c7d2e4bf0e894e78471fa89f727f99c2073b57ad0ab2b23ab429995f9eb947fdb176e28c2ff432eba5830407d95888ac9843beee5f7c224ecc1a40afaf7534b5c3010aa00d026e74c47892b4c803ccba015b1341b380f9b441eb79f26c0b1c8447d23f912dfb9d2fe28a5cefa496ff3dbde6fc9fa6767339c8035ca97086d6aadb88b0dc1321688e887d98b25cd01819655e9b1432200c665a0a21d40e7e0244f1c373a202aecf5ee088442b4fd2c5313001a02f584d75eb7b6e52776bcfe474f016bd692a2062da16361e2626c90db4aee731753bac3d68a2963f02fab8f43d4fbf52461e1737999975f52f29533837f361e89088c3be55ca975278143f7bc986da19f71c483e99e599ac5ec9c532fbd2c9dab0e088b6978ed7e65dc278a736e5d03792040ddeb18cea5bb841cca78a2f449e0b360ab2ce1f1dd4c8cb39ea12679615111308ccef9632654f64e19d97906c95bebb106b2700a865a5165b9b8ff470a3b72fb6a7f3a91f58d47828e155cc94dcdd10ad68ba207859b696ccd40783fd81f814647a611ec4c8370bf8c01b1ecc618a945f5432f2981b1c4b34c75077d83d252d91e6be4bafe7db360df60b17cdc52871f581610fd0a35442b466230b85830d294f38eee8d3f464f1191bf6f3697840817fab5682593db32bd18c3e35c279a623cd32c6a3098f4ebd0c1b8944809175090b273ca8fcfc6b2381835cfef50ad7135204c539b5ef18e1256f2107d81daa0665d4e3bf06f0ecfc0a1cf514d0aab105a9eb9f695af73b3ba0df3cb2d1c46b7c02bf108567e6d01cf49e6d767d078d0f7eb40d460e34d78692e0114c3f260886fcc7e00c8b3c7008ff429d1a53aa9c2e2a41d1cb34e154057c4686b4ad1d704d5fdca331c9f8168d21ee75a74a465224f3327845653d52575e720a797cda03fef68668ce7623ef7cc9034bee94b8b0181f2b11ce4e8710e7497246d272de79509f4e2fe6c8b42422f866c5c69f6fd41d41c9c326912a2fc919382d4e3d414ef9fc9ddf546ab52c8d93afc585b420390737f26228865603a7ae33fc8e19c944994028eb2a20b57bfe1a2f15f0f372fd14ef2c297e78695908b0d6cedf144040f14908d59b7ffa58b1b1930bd65270942bd8043e38037bb5213ccccb00db3df8a661e6fd31d165ae465810350f8182320df72bd876d111321de34fe704b96862982f670d697336dcd782eee7b90ebac50dd31b62a91af03d377532a157b4262a7d1cba57fa045d75bda66dfc194c0b8d09ef91d887469dafcae262b0ce0c2d8876e189885ee793cc622d42f5719247341ca89e1cdd4026584e5087558fa70f750cb3e58f15f4655cb2aa5c2072ab82f11dd0b47a22d769197a373b5a799afb067f275a5477a2592432165fbe5e5fe93416f0816a98599b5cdb93b09175bb78f688bfa15eef313efb9d49047188736c9f096d86d047d951f18de0d5bb2b435d9cfaa88521fa55553ac1171aa3676e41ef1387921e9df4c7c6e996e54033a9115e1159e9cedc6121ec9e81a1f516dcde852f17605c68e6a41ce312832b731dee504377e669986a4438985af9523357a9f41ab6b9d08bd5e05aa971a009802ce32e31c29228a1f314a2da664067dae295662923d7715c95afb00e860d12d7b168ae050cf29e4bf1ca4fd61b9129794383fc25db22ad3229fe1d00f4761e37e897caa298944185e893a6bceeb54e22b467102f2d8e3e1b2aa2658687b4f52d165f059f4053284f6f3797ed016a1cd51d6ccd1920f19badf516351bb00b5ac3bf3f00da9600778734dfe59ff118b80c46662518e010c4e1220cc9a38032e0cfabd5a5a5da75a1a827179c567443a9f95c14710c03c74cfaf3ba71459a2e6c84aaa0c8dee4d69514795029ea15228a4696961201437035923818557408cdb632f2206a5d9b67d82fb00e9c7c62a3a47c330f5f0875884e7471a7c2cf9b2ed4d9573c90e0ddd52b583e9ef1b23884e89a0c0c801599f00ff79b96f561378e455584cde3a3f5a4c84a86cb62c0998f4f6beba9ff1d9cc5d092a623abf9cd026ca12b2c816c0a63ba7666422d2609f8896501f94116a4ffe86b44b683ba8071cdd9b7db1f7aa2a76e30c1607e8a6aa03a5384ed982b6b6e809ead534b62fd2963a7027800b08e53c94f16a1b00b753322db877ab20fd39eb60b9280409a4e963c6e5ef6f66e7f8713a7690762ad71158a9c3698d42fce32038142ee603c46b0d6510f279e56c2b05d7096c2416cc3dfba58fdc0c50a8b5d89048bef8ad00d7b6fc744234baefdbc29faf5e4694f0d0bc87ea2397a0de33752094611077ec049e3deae0062a3d54d9c55d2986f356ee54d7ec2645c18012c3dfccee4fed0bbf219d4cdd93b94c2f1dcdd964a7ccba4954033a07815248455ea2bc5b096f90cb81d4b534a82a8c55f8df025430884c395c2992128871e87c67a24158e7d9f2e700b685d9450e5084256931384cb517ce80ecbd955671b13c2855124079a3f8a423b00e0512ba3ee812ac85cc596ff81ccfa7c31dea015cfeea3704b8289741c066bf18d60a8f6e453921513d10a7494b7a8ed8bf4430e906fe2dca5b1f2daf50b5151e78872f7f971fde706bfc267af4d6c71c43c461b331c16f81c053dfc33bbe0973f91e979d96622bb2805eca8bd1ca1c86a7e4f2222e04ad38f0cee9892e7f9aec41ea9fa52334b246d476203f7c2d5f6575b7d126f845c8396208049ac014e067a49c7da865ae3e17c4b4630e63630abc4958e1924a21d32c26166f01ae2b8da72d7a56e1afa6621287663eb5d2983187964f81753ae5213daad6800140e34f3af2d7e227cd2c5bd9d4758586f6a4d60cd0cc70034d64e1fb4d2ab6ed9be9b2810571f2189149d9dfd08f1f8abe05475d3f445230413f3c2d8d7a32ff4c27a59f3922a9523e81ceb9a2b0a35482808b08de05f2686e7dd5fd0b5cf19516cb0558a76d4d97770e28404a2e78dcb63f3b96eb19f1bbf291e88a6145eb27e6fa5ed3609e829094f9cf937c58dd2bbeac045e27afa19407b425abe489f434d671692197d5cf72c07c8e2ac447b2db81b8e0e248d621f0521aeedf472184eff3527a64c13f0c601b3ec2048af2672255c14010fb1430fdd8b36f426c6469ce3c6b0be2cac2ae4ceea8baadd17bd0900a75114a14a370e499163de5a0e817ec703ec5b636140892fa8203d20c032cbd574fc34d2385320f8ef889048179132035bf18bc196f5b08bc5e4d0ae4bf18768342d5b48f90d27c5eda7c200bb025a2de774ef9a8dfd0d75fd844969950a13f5b4320a250d4a124fd0beae2313ac372d0583b79543c07fe4a31afc144acf99238c31b3e0b9c6241225467d7ef6a633313309fa84669b737cda0632d2dd36ec901e1ae3e4a68210fa0b0fa02588eff6e72f73f015951e42f85868c622a8be41f3247a2140f3fc0a30d31f2379fd387240e5f306a17b035720f57dba876e1cd0d33bc87d966d620e65736370fc817ac4ff15db12147cc85c230ebef736047408e03ca8f0be6a642030b5627458aa1e65dd56737ab810b2a8b492586c42e193ce28d9aae97a13db0cc36d358f6efdfc379ee394140a6700fd99e86b8f7d3a594cceb4bbefff68ce15866cdfc9894097f08e89b66f8c30bffcc001044b0594922e11b8a055c9f6ee026ae31ac020370a1f3e06daa5a772272941a101c11714fe8d8e37d40c3122c61d8f610d194cf82da8419cfa1680eb14ee96b6686e9f209a00df36e96a9d6f842571b23c753c5577e9ed466bb585e8910722050a93a8463bb655ccac5d9900a31a460738ff88a624304a1e5b37fb9ffb23887000c006db10e2b4b8985770d88102ff55d36e7513f37acb4c5920fbd220fc20c8b9b1a83128b0c64bcf26f761bc01407ff23a9b11942b6034278ae1b4817169180989eef86eba3f781c6ed7c82b279089823170533c0791d57ca17f1beb9bd6fda0242993bf5be154fa2377340d0c0c8fcd0da22a25000793deaaf634421431ac4d940c6ccbe9265f52eafaa6660cffe9ce6f0ae13c38cb7ce5304e697ff3814a992c030ff909d9b33615c8f9be2f199990be4b96c115324f1f53c609d9c70c9a3d643973be4f407aa54cfcb7741124c48570a6a3906b2eaa017d1c4e98b1336932976e7ac4b2cc3ff548a3d5272ca9b66092c0be62c903cc5f6022fe892c37fbaaa5bba75f41e40b410b223b052ed39b580fcc936b9582cf7c479ce148f56a374df66ddb07dd6547ed67da09e72e7820676bd33cc3dcc778b5e5929677908c6f43d3f2e793b768cb2bb6c11cac258438c7630aa10ec88ac11e66c0855e0be63beebbc4a846a4210d3b16a1a45b07144aa8b8587d6d981683640bcdb7390599cc6b806869d01e425cc59f78d26e009412daa789b0872610de5d576f928321c80f83d64dd5cac4562cfbd72b56bd17165c7c60feb78c973289e4afbb58225262f916b2e189f9195aaaaef30f037d1a0b47a5e1ad425f16c581f4317b4a1cce0458eb64e01a2ce88067b6365f20570e7a6ef374c26e7df9d9b86f1a2ed3f3a6f151d108f24245346f7fbacc41e39d1b89bbc83ce93442bd733e4fb70c090135e71518d07f6f7cb5484256d973b5e107cadc46b3caecb4a0c85930d59631ae69c164b9b51d4af12b414da442ca6e2e2e905b3d35c848bc74803d93cdd1a52642f101ecb76adfbb324aab602cda06f4142f88ea187aa2d9bc2770fb55738bf1c22a0172dd3aaaf0756da7919ea8fd480bd1852cd97487ce18b5406f1e208103de94a56189a902b0e677d3ebb09b9b4403e78dbd0aa42f5e177f3c0e44bff9304af9cfc15d61d2b7172aaff7db8e31c401014a1d5637a540050a7c24f8ce92509be63c99f33c3e169be4e48c5c46c0d00536d73fdfaacd543fc05f62e194e64df3916521560c2e7c70651b0c8cd1711e13cf10ed6d7db7642d832dac4b6699b200a8bae01e5c271a7cb1dd55be7593252ec16d138fea76b7ab76eb2597f4847ffa61f1e5560f85d5b1dfb976384bd384137d0d599a996035b696364b2bbd910add9dd520e1d50d4acbaa28865a9eceb70caf6cfd4e46b787fcef171be5316b41061ad0f844d78c571948c0c3b9ca5b581e66da9006818668cb3024873fb6c209e388964e71e4e7481f60e98293614a491c4343db492b43eaf5acc1b24720b645f73a741b8082ec8e7f0940270bd337222e0e5f38ae454a0bbbb97e2684eb4c0f8665e2f5fb567a7fc8f46d42d76a87b9167d5caaa34cac8c6c35e92f339daa280b074923950ca4fbe26d12fc08df611b122b294ee8165d619809c84a8483ac031a91a321ba578cfff08ff1da3638da881a658ee5025ca8cd4c2412bd64fec5acce4a063c903a928a1bbe47a19d6f20ce2a45e48b775e956e7328e2807bdf0699717624dcc4bea3f49696076d6bd1f3b32648c84b627afc98ee7a255a9b6a7688bc2cb1d022b456c914d73c31a61cdf1f0990d46e50809e625fee7f66f13f3fd3d711053420474e2137299fb499c55d280fadb264bfa1c01fb103c85e1025144416fb45c410b1de94ce3b6b39e31dbdd40085c18596d5a4f3aa5d72f5d762f32657466c3d6cfa119e4b15d66c166f9235130b6b5db7bdeb688674c2c9ffe5953f913dc8b70c89ee3a3e9d0b900d3eb11b0c998e9028b8af68a3a123f0a5aa56f987cb287bed1b4ec7ea4d5080f7460191140fbbc521c4b4ca284d2f52ba94d12c57cffdf6b3a1823c8bec3f227f0964d0269567969b2a6952d67b2cf3fe4ed64ad32021de8df302d6fa0e5934dcb0fe0207c4eaf81924185e0ae50fa740e900c098f17483a8c8e9acbd142b0d3b8d7ed31222b7d9d9642133a4d710850b29be1b312f2043e0553468a03a468da16119a0147bc6655daaeeaccef77df33300f0304b229d0a67f2e2686ed0faf697a8c0be02a92c5eecec61f86730c46d3e4060cedea01e6ad69edab787224daf217fc00d25bc51eaadc89bea9ed8b8b064d00a603bba923afd50feeaa28480fe0bf5208e1f6c3c1ab06c7581870abe1d4263967bca1c83e6d829d9967b15b03c71662b051fef32423b6f60529ef22b34e79fd39a03188920fb378c832b94ae04ee9c1e611c19e994f3fa9a123454d8453ff1ab96e3dfaf32adcce9ce3a58d3ab46800283017c41df169d9030faa8635070293a6b4f92c2a03a23f322f9219eea5c13a65e16f8cfbb4c12898a9b5666c005ee1bfaa0d64396e1ffb0c89394ac75ce404e341592c70ec8ef696ac200a491dc290b060b0d8ab0b0d73a785117cb992fd5082920edf8a802617e40021efd904982120cb763677cfbf5fb26e040250ff71a9e8f0a74f8c8c0cbb2d83b70592dcad430759e5e3365da322b45198c34067f1ef09108461a7aff586e522eff5dd06ba39f32adbbb2ac11cb5ed218b080ef6b47ab0a21d7658d2c57a0b782547f8c5acce7c340cbc4e32291a4f4d261d51eb5d19fc313f13da04d155cee22bdda2a05328dfad96d058d2752e41372178a8ac3069d27277f5ad615312c8a0da2d6f5967a6191fdfa66a465ba370e3d8a8b27f31b75cc1d1dfc9e914df09e8044c0d753950f5a662bdec0dc6b50c3fac197df947b2fe927b02824b638eba85cc6b3a3a4633ea595e9e7c01764122a2a188c7f05b86a0901781fe9cf000e194128b474d8334de7204e7cf9ba4c45a82851cc873cb2d851257d3c067a363b22d3ffc4592989e1cdd50333e7d20858eaded8fa02b85209c329cb4ba3f99922719ac16e761fee300db4c4022070de18149af7d30df3be006208f5703c677000acfdbdab00aa099e0c515cdd6c36e4c2fcb14d4a169ac5226206d780418161648be96d949099a7cd18b491d72cb00baf6a36552c49578cb858004493fa5ffabec5316a34f8af4e3e329c28ace755c8329add179dd15a716fc30cae70fd816fb9ae67f0e595a39585efe683dac6569cecd5b4612600ca7e58142774ac04ad50ebf3fd3ffbe32cfdacc60c4942daa5b1dbd52cb4570ce3acffe92365dd10cb6526daa7ba1a0b8635167f7220c97ce29b11fff0e2780c3d43d82700b6511664384bafbc8ace53cb17529a7cbe4fa1e5b8242a149af5120092d5f50af044e091387602eb00690a21ed41b2b6cd2ea58ffd23c273f07932cea2d0e81664d0b1770c1a8726cbf09e109bb62cf0bbd05bd068eeb95e2f120470b4082e0f037bed194d5aaedd71fdb36b71ebabfc29ba3fc10086cf780c1cdc74ebe0250fa517cbde50830063cefcc39e7848ae2f4ca899903296fefa908e8c18e1bb8a1437ac612ce96cf6c0617a6cd6c6e8a631315a1d3e29d803b558c3a4274960d005b212c9c31e65649812c0dc066ae224f5001a4893fdad31cf49271b8df0353c2feb0c6796198d5ddff04951416833ce4200813f0f39d2ce75da2985d4329c1f2b2023d34a62804670f522d449b1edbf12c255673077c821683b1d8d7e5ddf800a4b7b539cba5dcaf1fdb336daec57ba5cc8e484c04b5e70f74ee3a0a2e954e2175117e97ac2c5bdc2dd375946a0355561c2cb8edb457d5185a93189e13a4aea066c51edf64ec0704d127db99b2ce23ca840435bc4ce9d8b533e23bd09c80f5004b23d8666c77a078368ad040c4e1ce3d4fddf9ed1b1863c9ed1d2eea60ed85f24edbcea0b513edb75f6321e428db3b3082154a7923b2776b96f9276f251b155188c9415d7e4f09e37f89fbaddef8410979efa36c6a1efe5565349a96498aea518ac498adad8a301e7d171c35883c89798ca890032253d77b00393525dfc1c992b73dcd9fdf3f304f8b5c1f7a71f4036d4095ffca43ba1c2ed25761063f0ee3925fcb36659ca28e692b6f5e89599405f990c57ec7237311d663bdafde7d2f81d0dcc1c522e2497eb105f9d9bb4f969cb1c89c2c4010dead0a19023f4bf15879c2f13afbd1caf80f6e3b5628c1e36f57189ec2f5a61d03eaa58e379df3027da6f24403ef8d186c62c5176573cdb2e799c65bb6c0da4fcaba21d4b8c02afeb6b8717fe4b74f45e45025aaacd2ee55901d81ab63d04853f222695e0969e3a9f89b9297aa44364a7a2bcd0be5759f3a27bc00d7a99f32b69cce48cbba3cc858a9fc708deb0b9515755e48edb793a598de521bfecb827ca3fe1e58b9875426b76a268137ba4e5fe24a5d7f977fe9d1b50ce573fe98be883ce057ff046e26d999403f52ff08e8a7dfb7f993a389212ea2ed1bf7725f4a265bdbfc208fcda3df33465c90928f1e1bb77db1955ef73583031893947c902eecff3fe24903319d5c2433e0b0546305540bd96a71fda765fcad36af678a4f07cc95fcd02e81ac2760931d40c132c998a8c0b120abd2d3e666518d1ef5ffa130cca13b78e57cad7a151bb594a1e394e73d71aea3c2d6e5cbbff872c98714fb5fb571e1e3743eea704b295666d21ce4e84ca11a1d673a27b169c5f83088e0ed344fd75dd4b741b05226ab93513305f4a7f83ded532c62d697e04ba2a963e30c321f916c0080e9fbc5ab619aa8c220f38de69ea6d5a49d99f904ace4f9c6132a8708e225fa2ab197c4508ff25c42a92d2a7cc3855cd59f6c96407266922cfb6f523d46e47e29d0447f32582e1951a6b0361e26226f8379a50f2d5e3204c6828da40b8e7cdc816f6b166ef4aa723077f7be16b732f1edab20a5668e50844b717dacc74dd94451cb9460cca88ad8a22ff47083c488ffc04e3b9c39ec7b25f41ee2a2a6cae755bc5d1fb9e1633549013968d569bb6c6f0d95547d93eba03a1da40766f3ab3b18f19b30964af1d69db15b13c2024eb855dd70af579f46fad36a77a77c2cf65812e92125f2f9e4011eb2ccf4716ddca655668da9838e29677662101824afbe875bd3490fa1a7a286ae31704b40587eec531af8bf89a26692686387927daec1d6e004fe9105160219a7d23035a362fa532f57b6269292058de35d8cd36c4c6b1143821adbe16379d11e1232646f4d497651f0954ea9a1df894dfc491003980f60b52cc0a1ab26756077227ee3102ea57dce3271965fbc8bd6e66a68a2b9543b577278024cd4536b2741226c3b1d409828f4e0d9874ecd7069da3c9c77f290915adcdf6b456983796b3fc28042c760483b93e07a9ac85a3991713d1c2f85b94556c6d1a273da1ee90265d01a8981245c7f7f8d10b2915126d9f9ac61a2d6a8c055ea87be9bafcb5643c9b119120d465b5ddb005f7ccba998fd30058ffa33d82c9d8e4e301e780bd7f93832604cfea9de23f1d6b2a50f65047ac9f879eff0451f311164941d680b76395aa208016ec3c5dfd1e4e2451ec13fb58f2242b1c26e54bbd88b44e163e4e7c7aad0b3d5897b5f0d8d49522bd7b5c1aeabc1faa5f92051b7c9609bf3dabb085a533130bb3f740556f8e71453758b79613a124d615ab01b2e1de8a4e519947fcd62b059aca90452fd3b086e6f37ec6b9c8c565885d7bc2125f2a6edc128992c986ed1e16fd93e88a03407f497ac742f49836ac0886ff6354b8a0d7096dd464cbbd9e1182fbc13662c46028cf859e93de493b32a8060828596b2fc4245655a4f9589c0c3e6a3130d7703084bca793e15a9e9d6366f1898695e5d47b7da2fe660461450c47a7186e240f9f600739c2fa2038f28aa34075563b42e39189fff917f77b1d169abf6085508100e18e9651f68113f0371c993ba004d5ac582763655c4df79c28668406aa60c8966a70355446b5ef97eca8f9c21cd311efbd65d6701932662d214ba804cb02a5d0c4e936c50435a6d8a2678558c26aedd38694ee726b9fd89a039a0378b17166714bb1379bb173d2e916411278d8285e9e6dc901a616904b1927280ff562b631da5d3cc81ff70e3269a358e8c4dca3696ccdb546ae1147014621b472511388d61cb1e60590ad87f05617f122417a4282d94bbc643f0547a6682008695507d53505be0bd9b78ed3c7adb01360df7cc3b4e8ac5357485facb49c2e0600e9a923c71d12fcaffcddc8de640403018a9a2ff1c2ebcdfd6700facbbb2dbc42db37fa4e0b64fa45559ed421cf9e91bbb8533983393113a75023ebc8090bb50f8d0b75903c3c8e4b51137f3ccf2b5ee42e549fa6c2c57a336718e1867420e656c75e985702a0b1d78e451a4d0e7abfe681ecfd92e1fe5d00c00a22acdd0ff415858ee7fa9d195fbd1071d672106427bcd2c8f5114e6b4e3b8f6fba28c62b38318cbd8088a2b956ddcdebbcfe0e6670d37b63276fa30a16791a3fee1b9d", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4411dfdc7cb6265f100524012f038ee1f205bf8789b70b46c57463dedb4998f7ac800000000000000000000000000000000c6895e67e144556602cbe13943c8169600000000000000000000000000000000808b3aef60f3e74a629e9331d68a14b21806572c19ee2f3532e970d617919b3aa8cdc582782dfad0699badc631f75128000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022c17de150116bdca2a3edbbac4f77678c67c35f056f9e969387b99b5c26da55509a23e762ede2ab19d289955f6fc34a289c4196b6f4fed818bca760158098e851b1e385ae72fb3add3fe838e63d0e080c2409bd2fe5f57f926a01a4e31278a1e0fd8fef8ee3dacae5dd4bd8666f29088e7e2c1680f50451cf263d3e53ba2b8b4000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } + }, + "test_exit_code": { + "crisp": 0, + "folded_export": 0, + "enclave_contracts": 0 + } +} diff --git a/circuits/benchmarks/results_insecure_agg/integration_summary.json b/circuits/benchmarks/results_insecure_agg/integration_summary.json new file mode 100644 index 0000000000..005a4e2c61 --- /dev/null +++ b/circuits/benchmarks/results_insecure_agg/integration_summary.json @@ -0,0 +1,244 @@ +{ + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "bfv_preset": "InsecureThreshold512", + "lambda": 2, + "proof_aggregation_enabled": true, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": true, + "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + "multithread": { + "rayon_threads": 13, + "max_simultaneous_rayon_tasks": 13, + "cores_available": 14 + }, + "operation_timings": [ + { + "name": "CalculateDecryptionKey", + "avg_seconds": 0.004296347, + "runs": 3, + "total_seconds": 0.012889042 + }, + { + "name": "CalculateDecryptionShare", + "avg_seconds": 0.020525153, + "runs": 3, + "total_seconds": 0.061575459 + }, + { + "name": "CalculateThresholdDecryption", + "avg_seconds": 0.019100917, + "runs": 1, + "total_seconds": 0.019100917 + }, + { + "name": "GenEsiSss", + "avg_seconds": 0.006925194, + "runs": 3, + "total_seconds": 0.020775584 + }, + { + "name": "GenPkShareAndSkSss", + "avg_seconds": 0.010452847, + "runs": 3, + "total_seconds": 0.031358542 + }, + { + "name": "NodeDkgFold/c2ab_fold", + "avg_seconds": 8.270900388, + "runs": 3, + "total_seconds": 24.812701166 + }, + { + "name": "NodeDkgFold/c3a_fold", + "avg_seconds": 34.927441903, + "runs": 3, + "total_seconds": 104.782325709 + }, + { + "name": "NodeDkgFold/c3ab_fold", + "avg_seconds": 7.542320708, + "runs": 3, + "total_seconds": 22.626962125 + }, + { + "name": "NodeDkgFold/c3b_fold", + "avg_seconds": 34.922820125, + "runs": 3, + "total_seconds": 104.768460375 + }, + { + "name": "NodeDkgFold/c4ab_fold", + "avg_seconds": 7.895315874, + "runs": 3, + "total_seconds": 23.685947624 + }, + { + "name": "NodeDkgFold/node_fold", + "avg_seconds": 18.310982819, + "runs": 3, + "total_seconds": 54.932948458 + }, + { + "name": "ZkDecryptedSharesAggregation", + "avg_seconds": 1.566955375, + "runs": 1, + "total_seconds": 1.566955375 + }, + { + "name": "ZkDecryptionAggregation", + "avg_seconds": 47.874825458, + "runs": 1, + "total_seconds": 47.874825458 + }, + { + "name": "ZkDkgAggregation", + "avg_seconds": 19.861393583, + "runs": 1, + "total_seconds": 19.861393583 + }, + { + "name": "ZkDkgShareDecryption", + "avg_seconds": 1.358248069, + "runs": 6, + "total_seconds": 8.149488419 + }, + { + "name": "ZkNodeDkgFold", + "avg_seconds": 111.87172225, + "runs": 3, + "total_seconds": 335.61516675 + }, + { + "name": "ZkPkAggregation", + "avg_seconds": 0.925223417, + "runs": 1, + "total_seconds": 0.925223417 + }, + { + "name": "ZkPkBfv", + "avg_seconds": 0.218732388, + "runs": 3, + "total_seconds": 0.656197166 + }, + { + "name": "ZkPkGeneration", + "avg_seconds": 3.515460263, + "runs": 3, + "total_seconds": 10.54638079 + }, + { + "name": "ZkShareComputation", + "avg_seconds": 2.332301763, + "runs": 6, + "total_seconds": 13.993810582 + }, + { + "name": "ZkShareEncryption", + "avg_seconds": 3.965180039, + "runs": 24, + "total_seconds": 95.164320958 + }, + { + "name": "ZkThresholdShareDecryption", + "avg_seconds": 3.440710889, + "runs": 3, + "total_seconds": 10.322132667 + }, + { + "name": "ZkVerifyShareDecryptionProofs", + "avg_seconds": 0.103301764, + "runs": 3, + "total_seconds": 0.309905292 + }, + { + "name": "ZkVerifyShareProofs", + "avg_seconds": 0.285312216, + "runs": 5, + "total_seconds": 1.426561084 + } + ], + "operation_timings_total_seconds": 882.167406542, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { + "label": "Starting trbfv actor test", + "seconds": 0e-9, + "metric": "wall_clock" + }, + { + "label": "Setup completed", + "seconds": 2.7038335, + "metric": "wall_clock" + }, + { + "label": "Committee Setup Completed", + "seconds": 20.174264042, + "metric": "wall_clock" + }, + { + "label": "Committee Finalization Complete", + "seconds": 0.003923875, + "metric": "wall_clock" + }, + { + "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", + "seconds": 131.619168, + "metric": "wall_clock" + }, + { + "label": "ThresholdShares -> PublicKeyAggregated", + "seconds": 144.272573833, + "metric": "wall_clock" + }, + { + "label": "E3Request -> PublicKeyAggregated", + "seconds": 144.78216475, + "metric": "wall_clock" + }, + { + "label": "Application CT Gen", + "seconds": 0.009741125, + "metric": "wall_clock" + }, + { + "label": "Running FHE Application", + "seconds": 0.000051333, + "metric": "wall_clock" + }, + { + "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", + "seconds": 49.457601, + "metric": "wall_clock" + }, + { + "label": "Ciphertext published -> PlaintextAggregated", + "seconds": 53.35583, + "metric": "wall_clock" + }, + { + "label": "Entire Test", + "seconds": 221.027911708, + "metric": "wall_clock" + } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x000000000000000000000000000000000000000000000002b30b0403aeee459600000000000000000000000000000000000000000000000db8eebcfc671fdeea00000000000000000000000000000000000000000000000af8f618d2adad1ebe0000000000000000000000000000000000000000000000000002c5040e9c20dc00000000000000000000000000000000000000000000000a2de63e4ca6eb0e210000000000000000000000000000000000000000000000021ae69d4a65c7a8e900000000000000000000000000000000000000000000000a005bc241e7d733b30000000000000000000000000000000000000000000000000002aa8c45dd85d4000000000000000000000000000000000000000000000008ff1dbd0f4b9b41c900000000000000000000000000000000000000000000000a46e1fc8b19f097560000000000000000000000000000000000000000000000075006eb06b9214632000000000000000000000000000000000000000000000000000093433350ca3600000000000000000000000000000000000000000000000c8e6e0c4281f705a50000000000000000000000000000000000000000000000043c924cc53c6b0e6b000000000000000000000000000000000000000000000009a11c5c7358610a3300000000000000000000000000000000000000000000000000001c36379475c900f5f028b1ca9b05f4fd64f348afe95c048291c2608303e91a5c888446ce265e275b7caf50609f9aed4ce3fdda4988dff1b9cfa755d22a06ac9bd70fe011311d3021f96a67be4ae494bd7f310637090a697383e48e1cfe83d199fea464581f5b26f5d27b00505d5850189be9bf43d8b14ee58b28a16c87b3bf9d5bd3dae341de20a977a63187eb07fb817d6f32bd7e353ea5f2e4563e05af30e41de8d76caf2a05fb7411e0736765ea872b88f93642e1a0a122c2beda192429812e49797d0d2b0b2d57c7f9a3d89feb36de6e8925068d4cf01c5631bba6ad66d9dfea89eef42507b82d0cdde68023f51cd886423293963e553af03761d6bdd7e82c5f3f70581726faac8f74102eb6129ddf5940c15a1f473a25099ce9aed7c8502f304535e73f02c89d7edbdc59430a931b60bf65dba6ff6c4568fcf3361abd1e9097cc3446930d256472545bebbdf0cd8202744ebee6b32aded93bbab7c8b07493ffe15d753c2e767d9b71f63f422e0d8397639315c74a3f459246674a141133d6068e91cd04236270fbc4be415c1b3fb0340ab79c0aebf168d36a35a77d26f5507817a381e82997d3d4e6bf47cf282de75db04e846b05a1171e1b27b3378df7f46c8a0d5f452ef0980db9a94784dc6878f8768320b7a5f1327d5a0398b9090cde4a0301320d2c66315a1209ffec932d6318d851ff138899b2fc2f20210b9ba6487e63272dc9292a55757e58a10eaf53bb32db4bd956cb6ea7db2f7d3d376ffde54a0c481c8e2b91e9e7d106a81edff57b8a49e69ba1cbce8d8ad7a768f51040cd0760adda1f1b057d97309b56258908e7d0b72e311d563d0f33a89b025e9b29dbae8430af6c1d5dc9be373e533150316757b50b3d7f39cb3fb983eec9d368f3997f12567c3516a615929225c27f1166fe0b8a910ade60dec004d39048fbb5e348d1d7b8f14e167cf8625eabdff7f7262e220e7cad7c3169f11ed47467b15790ad046ae3ea490d5aef76bcb164504f3b903fdde68d660bf30a4a023c0639d3b0df5986a898291ca9055a779c358e4526204e571e880645cb2634ddb604f97aab892dd8b2c61206a8ad742b79fdd1cf600bb1b100e239a25d525acfe6fb97a5c0a13dccd21c3e1be9395c5f4a8ce11c7c9adf0450a3dc13c753c8b0d37894fdb03746c10c45e92b11c11485fd5cca6eef8495c4fe4a8e89af5ce2321dacfb3de59466cdec2510156f196cfcdf53325e7070710c87d4451690eff29e4d32a8e34518657116d7ac1562534b4b1a9267d5813b831d136f643f2511cdf969567bb8528b3b509274e022485fa86f947eb6caddb7d6aa964114d188ffd141b79fde4bec77997e8de82e1a22e50c704d7e5fa81ad6470e6e610932071304bd9547f446e615d36f44ab582ea1f51c35052fc32ef3fd4004dc82b1bf9a39b670cbd3064b42858de6d62c0e202f260e6c01dc5da58f0f5ca5aadf80c9a9810e700b5342d312a883895208ed277759e4d19186345a6d59fff1e1b2df1b9ed20c49e08dba5d430c5c889d232c0dbec12765d1f90c1243fec4c3d67e1d37e80f7783ef9a1828baaceb6f59bad6211ce93b78dc48f06df07f27a2150b2b744cf2a5a06a0df954aaeec4161a424e2eb3a35a6b9f4e985ee41ca8ff60b38bfce07c646fcb9cd13c438c57afced26e28939f478f3c70bb968c92a82a5be84d9453b1121028cc087d1d072e7e6c7c0205fe83b1a01ceba1e0e90bcce21bc3dd3be897d503f20f49b07d818120c8883900aac83216fa6bf485c26476d03e6cad5643b6c5cf217f866e372b1ad25028ac239027d059b6b09551123523e2a5f42d33c4b330bf9b77e09785e6e48fa657361dc82de67104ad93500c9741939e8ba447f6dc1fc4191770b950772b9f1c82772b810282ebc72e0e003f822c1a213b3ce88f42565589de9c8a22cd5e3fbeda3c210803bc3b27db480707058c2df76c782e29c4ec3ed4258affc6790d85b95c0102af61b154df03951ef522e8c2e2d7c0e65d89bbbac33c8119dd9bf436def3cc2b3767985ed2bbab59f583232694b3a362918f9f72a99af2f0f5af2efcc7697c2d2fcaf7859f1a53585929269d86b3c8973039adcfbbc308fee491644c480a7c27dfa9fa5ef3bd09bffee4577c9bbc3690003ea1e3aad93e92fb3d3049d533190deaec9660872e7fcabfcddd95bc009081df6bff849124e9e9df109416fc751b0d2a98b66c8baa3500d82452f976c890f25ae6399064f155ecdb915f4d008b7a002f0bba3996e8fb749742a4b69120e029e62ad24d4f998cb586da076c916cb321f43d09bf02b75a56631867d73c77cdf9da4de3492a4b650d78b39b367dfa510fe1e9c7137dda94b17f265f6938deee104649c6163212b3f9be3a66234369c113a8e3a54aa3505c8bd668b9bb77dd5eec4e994e0024de8f3203d92f6f301e1223b57e5d4a69b68f6d9c57d3fd2dba27ca0bfb145ab0c29922b9a2b129f0e3f71fc82a5b8a4a881948245a642bc118320f72a2723d6a7a9ebb1bfe185529acce021f5fa2445c3744c8ac48355df7a41a8520486e45fce375c3c6fbd3b33bac3c2e5e29bc45ca7fd84f34893fc756f888c9db6e8d1b33d731af38f622c11c5e180602f9f504ce236d5dd7c1068c9f472f64905dde988f8e4ae7f199def886026f21843689e58f2f3dfe48f8f5a637b4a71284fc5706e649278419b7c8e538926409cfcd311cf3c556de1e275dbabc9e67658d311ba3cb5c17542c186a7da2ffd129684a2f9bdc4f2ebead34aa0cc6f94cbab3aabd66c8fd8bf65350be1ef08abc28a673680e3f8c7e41567d1ded0d1c173be5f3f0c7e080b2f1545630ef9644321ed718522ab1f7584f6a4ad83079823dbe5c55b19701cdf61208e783f6516be01412b7a8746a2c86c2cacdb91aa020b8d1db64dbc78c4889165e11b4aaf173202bc28bc70e00bc24bb8cbe3839f97793b66099528624e7d1c7fca00b911b707b19bc81d8401fa754ba2a05bc955437d15102dd440d1969450617d725d1c0baf51b3d8ba9c5943f28453d574f21fc2f9a08f9752aa61f9188a9cd8574593a82ac1794392b927399e2682bd77ca428316d1a2a77fd52d12be4d3f7fd57b9b33a68065b4112e464cbb09ae6d219c9acc029a295cd67fe60517b04596e84de3cbd1602082cab3609a950d6731f100664632ada637279bec7ead801d854a2b92a322b0f0f6f0554856fed457ba9fc25583f8f1ff8d03d55205a052c67db1a02282cc80559294b448cbc117fe65a32ae25d61584c1993159d4c2329f89293162f89ad02d91f842933045e44515de74b0f6da4625efb734d885a51865862a4c2d8104682c6d83cb7999c411b070ea3a5bc939cd54c26d43025654b66025c683116bdf06294b2eb9482a612e3d67a632d391609406b60edbc28b9c01c5d5b43796bcc3d007d36fb02537fcd6d3baa7bc9c3859846fce0d7b30d65860a5d8d27f6d2718ed15850e16cdfce67e1dc1d63b0fc45f1a17a0d9c7db71f689e3766e12786b8c2a2d60dc28eeadd07ade9fbf8447fdddc4f91d05b10d136058ad481f9b4a7e024a17029bad7d3a24366de116e8077da5b101c14da26867735d2524bcb9864778e02dd5dc33744ca7e50e87806b9d773d44dd60cac69135a5d29dcb4b93fe58551024ca2bae5c4c5f6682cb487546e40abe929304b714f50ea72c453cd38dd5e8692c2ae119ad1d9ef3b605e627d35720d26cde98e914c811cc457ae3cd5252b79e25893e23371561e89a731da3343d9ceeafb13593ccbf355ea34eba49f2e63c47289f58bd3e1fdacbb319183e85582f405592fe19656cd0a23a1cfe79d906f80d1efe414b0a22a209c332036c65cc9d46751c183d8618b839afa04eb21499cde40a98b16c72709e82be7312a6085b0563e940fc80e1cc10ad90615a040be766a204743f7867ec779c8a1d0d5fe3de9462f19bfdb3da14dad8768e04f13148df882eec36da9754f273e15636d808ea116523f0d567b5316bd1ebcf6c4de936d03908774b8f1d132154642b87c20ac9cd2de8088f1d0a55de6db45a75543d1adb2806b5975df484e4db990be106045d32642afbd7a492681e8681c84ec41bcadc6e1527f9ce6a7a670a20f260ae45d21aaaa489855dd747e5340f01a956152504a31ebc51009014c5b28e9e845c3a143d89302c81d5f94bd499250d48ddaf47fcec0afa7a61480263c5273b8acc667e784402be671586cfdb7b4db3f8a0f5e1fecd2aefbcf44e5f04a3617598869909c0aceaf0951635951ed4180a0a9b50cca1bb2b5b775f56a5b46a804330edd52fcbdf477485ad2f90e1aa0e3c6febb4f769eb0a4e62b793e1ac9676a35745509b1b9137cadb86a1fe28b0a00b1ede0523ef0229d5521820274634a635d398df9b85908f4ffd8533407e995995fb6a6f4f8bcf15aeacc9028dd5d83f265d2fe95d44afb3baa593f702b1937c3a27a40b6f02d7058a16f98da0cb85c8c6cc07d602960eb28aed5a6b5a8005363783c4c6ddb2010fd07d26daf701ec005d7765e9701cf8617925e4dc28744355b61d1fc2dffd272b74a06f3520227be984cbf13925584c110fd52798ddecd811ade62f107d939c1379e3ebcb8d71f09dfa48f3f2be201f2cefcc38345ae3ced3286cd06dec45e9115b345f3e78a3ee45db7f9170064864d5c09cb0fc3e8f5b7a763846c9b6a87d11798becb574a394dc37646282a7f7c7db3824cda09f9f8a5741937915979ac90b4f7b5de4d1eb4a3b7d6181a242ce8efe9e99513e83b53a66d91e85ac3412a40f2ed30dc99975a2a8aef2f11dcbb15473e41cbbc7bbb7adcfb89f3a9aa708170af029423d5e3e33e5bcd5bbc040ef8843868fc9243d05539ef570dbe1e10aef20fd382c33955f4bb9721ff64b91dc1ccb64d1c39f422bad3029fd747441ead524ce950b1a581caa7944b372cb78c0f9933a173143d3c99f2ef5203bbd71341e1b580d87c30fdf3e28038e52e088ee5d1729da0d34f16b6485002e49708b2af80a0440fec31f89fc13213af1d473835795cc991ac856248d6947c032bea69585093d3901d6393263c2e887bb58cf5f9e8dc4da7a20bca245b87607de9ea0620f096ccbcd54b6f3921fca613160e8e406d7331807a16d33650e418f91a75a1b49155d96ae9ab1de581aebaebcb2cbda8934bab4be6568e7c921e6ee1e9ce8ac5b280bf12e8b4976159f25c05e85fd4f48faa1e5042455da26784f0378846b00b1019ece0c5443a7276dce936601960c620c0f48dfdce3d7158691ade1f4c89e8e16e33e4c84777d3b261850f67e4cafd59caabfaea137826ce0b785a1efe2a1e308bf9cb95df87413d1bed427d514f8d2f1ed5215cc8cc01b960d2fe3466acb780ec253f4c5b3916132fce27c7d53c269e49e0d5f5cfc302c1815c83a39a842a4196b71ab05ffccdc4453eb6121cad50beebc9506f5e6e7a86325c089c1505a5d1565e4e200c0f947704862ea0bbb30d55ef3b0138243795391ea5660caa47102215bb22876b29def7ec75957f10b4173c7f04355665519b869e5d51c8f7e156b05af22ec3dffc3be18ba8bef4b6112006d557dbc866a984f59a6b4850790c67d09dbdd531ecb6baede53b5f37ec5bf114667e1d26ff037959d61dda4206f612f01df169922373268b1e7e6fad457ceafaf8c9c25e62c37454897d9732b6135712bee728c4eebc919211002d9c6f8735af7b8c0a544391a46f28c3a889d0f2e7a104554aca10104e8de7cff3649c20d0b1f54088096a7532b5019e476647fa05e2080741d0dbe713cbcaaa2afa2303255df9d8e0d391a6532b96bc449d704437222d0edd1fc730ed424ad8d4b5a39369538c7d586ba3e59184dd5f2902ed1146e1a01c7b01250ab358965ae82a0f4528fa5ada19814df0d73e4256eb85c0782e527f1f4d0221ad31b598531d3812ddcc1499f25e1501865e17df02e1b8cbc9909234c2802c41b8f3e59bdcaca7bca354ac8a06eaceca80e576276e089cb5f29e61b0d1e6d85b56c11df39d49c0836c0a3db33fd564f9b81b2dab176127f2020091e84be762866d62955eb34adfb2c036dfdc79652bd5168e1eccebbc106c6e0842a6ad0ccc37f360dc838e8fd4104c57d8d74b677ebea4c070a1d5e7a7fc219ad1169ddb19c6d2d9d077f9a31b480259d7c4dfe8e603ec1953634fc56a67550bf2f4c127151b656bd10e7db9c9b4e19e526d93e483d2beb6d6f95d5dd7dc4ef270edb572eadb4f1e9abd0639860e241ae44ee499db89e92a7971670be22941efb0fffb8af56e85dcb0a48c09ae10131bf2a64b0ecbebfc772259c580506db69ac2156fe9c7240f7f66d4714e18bb20c07f44dc0cd84f0d9b1aa509b66ea5e255e0648ed0a2962ede02665952151a54e8cea60f1ce897f00a3abdcd6fae82e74b6212fb5e7fae591d70df03de924b74918ada838ac568031697a44c273484fdd102cdfa567eeb6b89ef0d9fa657e8943950256b1e7053b0824e4bef3666272ac0314f463083e7bb01bf0a666e722b81cb549429070f0cb17f6cdbd435b5a9beb8a2e4d8baeeb9fc7abcf248cd7dc6a01075c51db5fa579aeeab43a582b47399ced1f5044f92a873e6418ba412cfa0a8a2db3314585b220ed790f35ea358f93a7630fa379be8dfc67d5eb98c3aab5c2f533d6750b19ea957acc6bf9e2796920007f14afd5d25310bf4bd31ffb348452c2d021e55f237a212ae30aee3f52d514f4520a9af18e31091497248753927f99eee28859559b268451ff74aba9de02ee6b4322d7570ff915b07f2cb9c60a3b39cd3b93d33e3e05f149db531e7da3325e7bbc1384880b0aae07a0d00123c89a8dee209bd57008f71bd1a6a5778e3dc257782b1f4eb8695e6c34c7d409d48f8dd796db2afc7267b6c14299be5aa8fc680abb4d14671db1342e867fff13ead25dfd0ec4deae57cb9c3dd91d62eb03725d734f841d9749ba103d39a29deea0e29fae6d44193eb8daeb4b06ceb487292619b51b15221ac506544d7bbbb235a8b1e025e6f95a55d4a9da6e2269cd0fb34f9990ebdb13cb608f09dfd348a611efeb3a20ba7d4911414ef238b948281e71b4aee677131b1bd90b800ded9e14d190e183fc69564ae4f8e218035a2ae98adc09e2dd8911283d549ab1fd630223ac9b69f13ebd7edc52739fa0d86ae7bc7edfa3923fb49f01b6905210ff375d48be2446ec8ad19fd201030784ae30ecda07d819104ee91e060649b3d0c9e9e2b984ff05a62d46c6e9bbdff96c343c82a6512f86de130e771a1a878e6a41a6ac0fc0788e6b068a9acdf76a9e550c760f1a7d2054f006a5c71ac65c82a640ecfdbd3853275aa0ad3206b715cc3ba60919d7a2d307a318d84729e3e17fc47339081b3733d471aa0c4a156a7b54b1b9285d483229a32bc6340e223d87b4069b04d0fbbc22d2a54ac37af4289e9138d2648099c28729bf009c2d28faf64c8a8042fffd8bb899554655cd463010c1dabe9bd2d7310594d704f1e10ece778e01fa0d14bc72a9095f381f288abef48d73e1c8f70323019bd3790cf90609f16cf84220713056f45f8783d40e5baad22fef47a7e37b46ff86b16a7050120dd8a0182fefb468bc5f043da2c84b3c3c1233743dc0d3f812011ae063c60c20907d674016e758411738d08cb7b38ec5cf2675460d6e4979a92f1a6b69a6d80f09ae9f8647a6057406542e4b5cad4d751ce4851fe2043791ccf35977c9694b27ad7a862ed2c107a8ccf062000dc496547dc6b4d6f3b0ed91a689dad868bb70009cf51ecd1b12f4f015bf9b58c6a3473e82736e632d1274d46e8fb9aadc92ab01e0c7c4c9d3ffe24d2a3ea98e88d74bca4b8fc56d7d94fab738c555f2cb4ab724de351101cb87df73b68d48f182811d7bd724087eb2d3bf773a92ae7fb3023d05f7d92e780bc1b98e1bcfb57bbed3078e50104684c04a0297a1f054585de92e285d2c460dd30427ebc79eb4e26c0479727e1884df19ef51e10dc42db993de31190d3d6c988c1c3dfa4e0105fddb7ac35563ecc72a4a196e035f91148ec48a4b119a13ca4a98e0f7a165f6547ee9750bef23cebfb0e79c139d02c6565e92464618471f2483429f5093edaca8b65ec2d966a66317e4fdfa8efb3df367f2511fd514737a1a937a481e7d8cc9a03c3a60841a4bf7f9754bf2fbbe5cb6164ab948c00b2e306a251f23b43d7d25e038afd9bb94e90efa334bea6c0c33b9e527b154f0271d9823269f3414e5b7274cc402fcf960867cc389df326a885484a21a1730d30e0d40999fc462c52b6a00344eb43a6e589008ec5dabc7d3b7eadc04cef996180edb9f5dd33c7c3ec5e30aa494b252658237e1f49445b383aa5221481bf279c824a408a0cba2b07ae7260ac3a8b16e1d481d8bf795611beb58cf4994317e4a8a1f43bc309f0b9a6938bec0f485cd9f959630aa5929c9a94e40f0fa18f3ef52d225323ffaecbab738f0a11859f83e7823fab2368e875eb74f7efdccd9d03906d70bc078f6741689487c98d8823c61dfe17c86e49cff25367cce5f92cbc602a350181120e505a5904a146484af259d3cc7407766ef0f9d0ed2e8c8eefb1455df430b15d7a20b2b8ce47711235c340fbc0f7cd120ab132ce965274b695e8384b0af2f74f1a79885f6871a7fa28a0bc7c26363be0e27b50ec7c8fb14c67e80e2b47118205e0e8472e0dbd61b66277d3439fac17951af5ca2d6b5af0d524ccba21b6b2a6051dd94a1b8d7e7704680ddef09a1b1c4419f2a9ec9820da589e9b20a70ab24d89962987ddadaf53f6605cd3710fa08fd60f3daab58c4ebee1ff8f0c7b88616302942ef09f82722e003f1421f3722d0df287cad3f920171162ba91a98d6032d74219ce8c4fe3c7e33b0ffa14c83434d58870d116e6c36559a0c5276bd3ede2f133f4b9b09ef25e76cc4ec02d3a62df652cd9034f372c30b66891e912e1bef1e17181b25d1ba2eacf2827897b7b2d0c5995ca072785082d059c8644febf9771752e9b58e4c98cfc60c257c9111c8b2bf6734c7ac89d2bf8276d6eb14a2be82084e6fdf8ee543c1a3922df2d173bb2d7a7124e55a2d39ea70742821f08726c31b6c80e32786142546b5c35a2a9a2441745ce704f0e55134024b64036bd28f8d2b8119e8dd0e5cb9245d8c9058a66731ae81cdac1c54debe97ba687b26ff00671d6b69bb7f1dd005c20971d3f86f8949ae90483fc8972830abc6481f17f90547263749df4c5def86d1923157f67fa1738bef66ca4fa004e6fc5b4af721837799245cf824a7f53cd08d243f550311fd4a3d959daac4cfc85f32604bb35af71ca004ed6051ffde6dc1b4fe2eba6d82661319e9cd3b855ae418d73cbb37864fe3c10c42f2fc3567df4971fe7a1774e33b8a72c9ff4a7c9c666df10ab3fc86bdd66d2337af6fe5478206b02c5f1709d87b9df0b8fae401bfcd98b33c30e01e93675f1f355765c7361b84de13e9aef9e4fe7aad73a2117c05f9714a0d361e693470c80506dc72a579afc28392a084af4bb406e51d53782e6ca9ced5f2733ad62194041187cf6819f495e3f36941d42ff64e68b479b4c98047b50d14e9d906b8970b9a1a85df2ebcd9f91bd9f9e23d6a103a4398801cf9fd35ffe19b1d22551a09c77e050ac86b551234e153b9b1f200e5e4d128ada63380f68aaa80df274d0bd10de6129ec811001ddfa3cda1b108abf1a10645def9a8f6089074ff283c624782d8e507e2120f8237004abe881653eb8da68e34bd3aeb10e4a19a8fe7684dcd8fb2160e98913519c62998314bbad28005c4a83290a62f9334bd58031fd66bb0db262019b4cd0c0423184d42dd0524ddca8871512144dcffcd066ff6ebf9298ea84e812ae69c3c6009d6328449e1f9eeea8fecb6b9e5c96a196891777b729dbcd009b11dd6c86933cad747984c5842202dacdb14319ca09f16950b4ffd8e8380fcfa9223d511d82ba615fd3506e86326f1cda9dcc1cfd9e6d5f1ecefe79eec809734300c4b915bd8b2f50d9ed1228836ea07a31376c0e1f4bfd053de47bc04acebc1fe0930c6a3476edb691858be52b6de2b01d9e1e4abfbf3d3c5d2684b4bfb7c657919035941a2aae3ad42a9e63465e499c16d2dfab35f248622e7e58e84bb84b71d01e032044b717cab2d569ebdfe7d5097752cba06245ffc7cad1b18d79eb62ba3118c1524d59d9cefeeac71e447831f83c6f290ca10481e6916ae350b88d60f3e2f0058bdb51489572e95e31fd84ca7260abc39f36938b4f87e6d4835f1ad5fb221a652e8262f018bfa3856a8cbd34144e6976d45a453f3ec06fd6e376dc4cee60c6500c3280e6ab4927048494018c8afd9bc6fbdcecff3018ccf7d03b51a6eae14b7f8520c39787f70776235f01793d7e7f745848413062433200ef7f8cefee602784ed98b2c9d273cdecb02ddfa18a8a661d741f3bf87647e3b8d25eba2360217f3b3108225afa2797868ceaaa866d663881a0008e416c5f2f80af7149f93050203465a7c2fbfb1ae9517fbaca023c9862be40b2c84f30b81d237bd7c3798c30891c8ac7a91dc7b0b69ed9e0982acb177108b867adebdf348d894dd510a0b7e1cd010d9147a550c8f12f60691d111628d9182c83ce9536af7ead503f2d86b1b07c40fabfd7c29d47e4f8720b7806cee007281fecdf927c5a8090fe200db873a0142f5b631901ff537e84ad96b1796bb469b1b11cbe42c2610dd49aa74187a4129a1854db8be04d0079d6f88c31d140eed4d5a1139b6cf42a9b792ae577cfc7a2879bf32ae3070900028e0e30c27d2c5d1e44d731bd3d2e6d6135dfcfb18572d107551dbd61f608dff663d88629c18be0406c8ba65f8ce3422d544efd0e1e9000dcdc683eb0eece6d0ba89d46022e636bc1ca13b2918d04900ec63e29f630e7b09d795652cbd6a6479cde8cdeedcac973f5d240002f8f6df8a75ecb9e1c6d2b609938753bf2c7da02deb6f5f3bccce5e9ca2afebf91feaf1265aabdbe2ad99212954377b6f55c3faa8c03088b93f943eb08faff93d5c958f119889f11fd09ff304933fda02023e503321f3311c3248fc12cfcd7e748e21b1002a39c02c01d09209a0ce90d8026f074000e455d119e776d349ceb3da94cf523de5dbcff0f48b0c1a8048441e7c85fcfbc208ede9faedcd17cc3feecdee4c70aa042c6e441b059a14cf0077b5c8565e3c03b57181839f9f7ec98fb603c272189a6ff3a1d3a4ab621ee17eecc490d3d3fe3b69830871d0f9879d3049ba97804e641d42ee31522ecf1859fd0e2feeff6c4515dec6601e327c6ffebc6958cdf901c0ca743f82214679087d9626e792e9ee6cd489e79bb805d2da252d55a99150fe19bfb245a1fe42170dbbb9e93f584301289b415aa57fa49a31d5a2373d55a75df54190b1e216324d0c11ef5e89f61d21a42d90763ef5a89092a10e1acfb7e523d7f3fe9f8efbeee02c371b165637262eed95245171b22f53990e63e5d7277c2d73a4c391af7f2e4e1c18101c45f8cae2939e17c2a4999e38be4e8683139cdd427060e6f476809b670f35b2c56491d7f825eba12d6626da300fd3c80da328f0ea49b767a41a88ab8321c15d42c0af8fadf6438d42bf60c3a2b3adc5871de99e21556ddba86feeeb79168a27ba4c7f46b716afd29c915ece35e192e47cdff65cea5c35dd963a7b9ba5232bfcd3e8240bf1278b36d6aa6dcf916c22e853761e388d3f1a40a433aa34b506bff60e623daf6dee09ccd69d2781bc4d993565b6370e7684d7aaeeb2b1fca601357cfc35f67331a5714552e9eb3dd6fbf2119bbbca64ea76c836020d19ec9a1f48e6e38d20777c1c4cb8531ba222faec847efc90b465e9c34a7001ae1aae1a1b5e613f47a7d2a35f2fcaeb1539fa3306807755ed92968758d518fb4475f80a0a3ca9d6a6143384cc6083082d311bc387ed1b500266acb41b8126dfd9c93e1b1fbc2e1f174be1d7761604d9964bf196b9ee0a8706f863e1822950769d6662051d0bfd2686444c417b5ceea329e2584442c5bb2f75f8e7c180fedc7a3a6dc21014bdfe5ba28573f6415f77fa9325bba1727fa321aef3c424a762f29b3bf455f51babf7c357a4e5ed8cff49b4eafd2746f73fa06b1d5ed4bd15999b64d6f3a7121d69a9229292044de7007ec9f717fdf09dbf3b367ef4a7a1baa46b10b4e16ea903e4edaa9011039efcee40df9f889a16f1d4f4a9a29abd4c5d587197dc1da1d8130ef6d7f55840a3d60e13734ce26b321d00aaab7088ae1779d826e99d92f54b0b80606aa57ab42c90e751dc5b716baff6a81bf135f4250f00aaa0df2c833761247a34288a2ea47bb0bc836a6fef419c37495bec860fb6529465724011b4e58017dd1415346d52b19101d5eab12b19a0e441575d47307cd7dece7b39f36fa26a012112fcc778b55fbd14f16cdbd3acf9a9169d544f15345cb6173467874f657f11a12cb8d9cd9c39420f84a35791fac6511b78d593d42fc2b8f5d872900954a406331151227f3b4ab61468947e78586055c0c09c0b98362664e5e1c8975eeaf02cbe3fa2404ac419609ca705c7393296997f81a9c8423adfd4de864c57a97922159f4fd88748365f7c0d07563e4d453417656c8833e29d6390b86fe4b794250a151fa247a5e1b9c58149170293ce42a65b9cdd8b38d9ce0ca032a2513a10b9aa13bd582849621d7b26e71960d6ab34544826f40f4772edbf65887cf9c90675a02a24cd768068be5feb2b8020cab1f939df742e78b85890a0ed8735180b8dac5a2cb32325a25e5a55c3dccdd36db1e560cf06d8edaff94c8f8793df454115ab6907e1433a2fb8a9710e410cb4ae29b749c5a56812e5d7f5071e3f281e22439f66040be430da6e3d00d79a926e6d938d9e102c8de762f46132cb877c9cd0719ef3124a78afd322abf925c981c80564d0990d6a8ce664e2e17c87b4a0f4d853162214634684c7c01135a65a96a2dd46dce177320faba9c4559efd833f3dd5086ba8112b30bbf4ac3c13f437f5c875730809567be74cf8a9c816f4fd6716a9b82a1c0305ed2a51e4878eac2537a240468d0bca71bb3db3dc44587370c898536d53d80368b46b639edce91842b66e208b95b7592b85d9d72657432787fe6037eb85211c1c8adab3ff2f751ea8fbe3a00085f306569ec17b74af594427ec4560689fb70b4e6ed89f6eb53a9e4512918249b9551e0ae6c9f36ea559e9e512e2689d090a17415450b584e5c7cca50b33fac3b1787034d0208acdad636fc3d616d98f1dda10eb9991d957f037ea435e3319c366ef658c47fba775755c948610e7a384fdb026d34b79b7c65045b283b693ec39a3a461580d5e888e2bac27ea2c619e06650b23ef7817b83eebcdf4c1046def9755d8aae19209f9ab2556c7c1836228399dbd063a417e004937c4e8f1238d59628623dfef098ace7e0e94d254f75cf60370991373e1aaacd754e553d42045943f1356cd63e1f3db516c660ea72208a93925c913478d2a2d2d9e8f1fadaae136ac42187cbeaf41a1fecca67b1d9f8e45b8a21509b73b3cda6895c83a1346812b5ffe2c1a73db6cbccc95409b09f062caa7414f2a7b9182faddea8857099a424d6bff704d75eeb4793e53fd6c6b392efd365d02217bb0a66eb65c8ea113ee6800c30e14140d1db32a753de462a44320d257b6c021f6b2dd25f7507d6320a391d0b0de1d495960f32f421d11d91c7ea58f1838f225e75d81035ec78aae5007a650551a2130d8380a88a42cc5f00243adb65118f62bae9123494c72cc94cd04a9a48393508a4042ff5d4bc0de75cf5827c32aece92426e6a4df489f63ffd696a5e09c4569596d59d11853d305eb433044f7c58f0208708aff7ec7dfba646904fab95e07c593ad93e76728526f8818fd7aa58402a80feee7eb15a2f0b3c4cdb007544c53de0e1f82c11bd9d37ec0647180f05a657c2933fc48c254c582ab7479a9c83bd7c4f8eb0e29efa3ed8c4095fe08a1b447db0a0b2c4929f11cc7d7829662940098723811cecc8af257c9ca591eff5e55f15c1ea2954f52b5693fe6643d48e9f2f23f2143777b0cd7c33e824b1df67327692829046a46469f24a35f7a4a268c8071d3b08d4de93efe35e71b2ae80a054ae6cd0345a6af2d87b666f3f5babd0cd2158f9f1d0454c258d701393f9c43f96de381042f38b49a73be70882662b7070df771bc438452d607e3b501f82092cacd524912d368f485ca72dcc9607f4b1db83a3bd6b6e02d235791457fb5526e68a78d321645077c96714c920ce0f10bfbe8a0fd9da0b9366ee0db2b686811ff10cb381d13e0536e73e9b49c2341023a2ceb8651403207aba6394c9fe86be75b1f3c92342221fafa5b188657ec49279ea611926609c5a0a8b137d58691968eec74595db30a5eab47f49335e4f7b0e1c82e6cff21317280e2cdc9c35446b64b3c2295b7ec14cda916f25e16626a75feeb120e95eebd9754a395186204d03d9241a401df77194d9938769c163b7eda901948cbeecdf3e841abf27b3b8c92903ee36dcc5d060953aff4f2442313a8191adb4c9b374dd9cc5f08e8ba058b5c03dd111f489e461c1723525286162fc0c4846dbc36eac9cb8d32c1a121ebf3d81ccb322277755a2e55761686f53936d703806a52065735bcf568ac9d9adf605be45a56b980d198260445b04842fa9fd7ff6d367f50ed8553de4d1743d992577279a8d76586f479110cf8e5875e0c4682626f6a1e55d9116fcc4bebe725bf5ff24704153d8d7b94", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c03d33039d45432d4999b9964018d8d6552808849c83503f50a70d594c01b441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000c6895e67e144556602cbe13943c8169600000000000000000000000000000000808b3aef60f3e74a629e9331d68a14b202c2cf87674c88d6103108c767ab2ac48e35bee25bf5e92c02761b47af09b71a2c17de150116bdca2a3edbbac4f77678c67c35f056f9e969387b99b5c26da55509a23e762ede2ab19d289955f6fc34a289c4196b6f4fed818bca760158098e851aefe0609e27b1f5e4ba46cd640bb7bbcf5d11024b93b2b80830d9827e59538f1b1e385ae72fb3add3fe838e63d0e080c2409bd2fe5f57f926a01a4e31278a1e0fd8fef8ee3dacae5dd4bd8666f29088e7e2c1680f50451cf263d3e53ba2b8b4135a40b6144087f868125f387412d514b5747bfc6d2fe2bf59b31a94cba4d7b722725d5e5594a87fd3ee32e41daddbf89c75efb45a474670af2162a31702ad09" + }, + "decryption_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000a6a7b38193b9791f900000000000000000000000000000000000000000000000938adfed18caae40000000000000000000000000000000000000000000000000e21f5e071314e88420000000000000000000000000000000000000000000000000001dcc3d8161f47000000000000000000000000000000000000000000000002e46c7ed7ae9e45950000000000000000000000000000000000000000000000006b452c19c9c3a95200000000000000000000000000000000000000000000000f937207f5e9d7f27200000000000000000000000000000000000000000000000000011234ce13989700000000000000000000000000000000000000000000000bc917c80a1df8a9de000000000000000000000000000000000000000000000004158d1dc6c5fe0793000000000000000000000000000000000000000000000004df57309713331b9200000000000000000000000000000000000000000000000000007698a06035d700000000000000000000000000000000000000000000000b5363c615bf3049790000000000000000000000000000000000000000000000084836ce0c2626f54700000000000000000000000000000000000000000000000de7f5f0f37ffeb22c0000000000000000000000000000000000000000000000000001bca3c66a8927213f200493e130f2f241df6f50e717977080ee976b404ea52ce9a1739a6538942186c4ce5d408bc4469b54741acb17f81532bb104e167f13788ca258657e19ad18bdeaeabf611459f9516a80aa25dae22a716359feb8721e6f73ce82c65ed90504097a64060c9b98a318d0db5087034f8fb8be44fa54632799b74281b02fa3201b5874dca01aa6c15157bbcebfe698062bc743018cbf4c1838fd88dbc7725f1e2cc9baf93dc954f7630b60d425d36ef81eb1be7d8436127e0444eeea525630a31058031e3a95f311de89e96eed8d8edd125cde39fe180ff58ece8d79e1d4b19f2d6403f1677a3e0f98f7d8669a44ee9d1c5227bc6dc8f7fab18b5ea38a23a8a909d6b647f178c12bf5765e4f3fd9e9353f1578914f6289ab3626968195e2204a0b75d68cafcb752224084dafa2b4ac94248907a3cf380066780a921c968cac8c1745343af4f66eb1673679b9c00300c26624904c97f542932abd632e9d9c20b613f65677439219bc9826056b26cd285c1f6ad8cf48940c417ae82c3b6dda3ef601d957cb411fdb0e5a236ac0758e0f106e5c2aea4d997e4e082051f36f37a07e193e06fcb0040e75764cfe7d7b19379e865f44cde8c4021fe7a2cecf7276e482125b04ce7829f541ae0f786317799e17a5d19b18c35032546d862d824f01aa2b2ec87c35f2af31bba08cf65076c41c5b2a532fce570414bfc6619bde14f1f90519c766248124f4e391908fe40e4f4c2e47d959872db00ccc6c66104be6e3a4a01509488b91bdae4548075814bb3cda9893c4acada580aa2c29959515a57de7a31cc4f2c4335d29fdf50154c3993f6ae9031d06f0541f0e718afaf53d83631efd232f3df1898a0799134d1f263b0bfd6dccf19c72088d8811c5e361ba2efb54be181628501e2f6edc49dd9c2140408a2fa36d107456bc9ca11d66c1e426b5c3300cd87deaf468a61d8cd99c96247bf074714476fdffb493aaaf976d4dd235e99b154e79b2ff5e50ab7f8c6ed0796649601b9c5b5efbb5fc5f5b4f216469ba83161e04612689209e87667d0a819663d69a261da2a789ec0318543355b471178ee51394b1536f2652a7fd5ae364f01c53350a79a0a2c030636f3c2b0b1a4f11f8f70b7dd2a2ab687739334dfa9d83fc290414ae7a1369b7b416834d3971e6e4d10c1e739ee4f5603547ba8ba72746f349ac646d2ab09e4faa5ab7e68fd55609c6fe055052cb8a99d490c94cdd0e20fda8125a7fc867867440b977632d440bc72ddf2a79774bb4346a8defd31709e0d3be983bce16ec2f1b6eb0d064df61d33fe4eb0c0a9a31f221442947a6eb27a710db271537ecb73fcaa4ddc4435b85207362ff13ebd5a7cea692e2c11170b8f0e19c3996c9d4fe03f579bee10705ba2cb6a64120e3ec3fd8a3623c614a7233e19fd586b3dcddb5ed038a7b87085bc0d01f35ac0a910d04789ce430f092f249b8a95e3f3e9e57d464b7e39b11b0b2ce0511a53f06732a3e7d8ef4a98f1c0197fe86514ff68a832af68a50812a33c5b1b3d7357f2f0fcac439af16b2c3c51ccc318db8bfedef50919ca1df0ecef4cc3a1295d63a16e24b4427e5397d2aadf907e4021dc59573232a6851a4772bd0d7a9511e5e940c45c13faf58cea6eeddbb0a5bab2b269c8d205cd66a41192cb1a02c691451c60cd43ed39782c235f5b7222db00e52277c1bde022d68a3dbef6836899945390002967f653a03db955a5c8ce104f9bded599366c20d9e4e10815d1676c7de75591876c3b768ca0c50d93bbf03fbebd73298b550e6731cc9df74ce6b043d391fe501dcac5bfba80aea4039869994a026ae689f9fd7bbc45b9da35927b07a1400b8050faed5f52a92daf28e2f7a1e113694c5751fa2819d425c7038381219f561d31ab37ed6c11585362715d889de16fa9c110bafe87a89fb3437d64c75602257e3111a8aae71085083c00061af68a50a444442c811ca0702b1ba59efa80707a12e0574243f7c35ad11d0499bf94e73ddb1a0551fd5dc7137b31c2cb4ebc6845b5c11c03ef720b358f0b3417c3073e3d486c03aebb7b020db1ea2ebd23e08aa22bd2657df69bdd20d3023feef2bd7b0efde8763523aae1b31d1bebc86a1577122722af7b9eb04d62bb5d2dd340cb78eaedc412be64449d0314cfaf30771d59b4913156f4b94b14bf9ce19924e16e516ff1bc66e6955224645525d4eb36650422744014aeed42b2f48def893f4b59685a7a00fd42ae1856463b2ee65aa8f09c21def2f6d4a920159b6487b8928519e70a5adaf40e615c04e7b549c276109ed3888e808c5f4a847d257ea6478a544aab60e354c2cd9251e4456ba225db340d3c35b841ab4876ba9361592e0e2f351f1cef7a81e5875c24f4deb7bd8720b702b4d02941825cdc9b2fd2f8aa1e13b760f9e5f6b87dc45dd74defd1cb013a8894bfd211818a2926b858bb7f334bded04473e5c398fe186bbfa744b7c667066c72db7d9a32d8649606e140be47dbd386381b1fbb1a6238a8c38d9346a624d27d057ca8ef71e68e91fdcd8345fca7dfa89faeebce29c454669b769baaaba0fc712dc541ef8280fe2e79458e28de1f0312d7a3dde0572fe68bbf86f046faa8c2e07927b080f28ec7285880a3c089913308ed80d9c983352b34317079dc36f024c88cbe698f1199da4d50f7601e1a92ce7e958b907c3d08ec1c966d10f838864b06cb903a26f183dff4905cb4bd6861c48fc2e538b3fa22385197567809a7167ec25251e71811b35520a843903bea42f31b157ee343aa5335adec9bcd3120a6d1e4edb158539292537016521f1e1719774bc3472bf8cea73aec615aac019b47e7d879642f462263b44a7a876820f0117f968f8b0bcd1478699159b190360eb331181a2c04be40bb9562ccf1693476d7beea398beed58780d39123f31c1c401b3e59502899dcb070a2498fdee2da8d6266a91ed4ff61d016eaac361e643de776750e0b5e0675313820988967945cf62b69b46bb5aa6914de5556c053dd102f5065ef07c881fcc2858d7069c1c6f7915d99118c1d498f3232b2fbe53f19ae68faf49837c761efd1110cb2769cf48c492062503b64b03bb339905c4871ba162089ecb07e88f922b23a72ef676abd989368ca5ca6efaca4a3546caffe57f71aa15550539f41efe8523d17d0a9e67ea0a1f0e79adda4f7729011eeba0d13f56e2e03db3aa4ea0f1b62ac55a22371b0798281faa7e0ebffa3f57ef9db292b0a30d64528ff64c693dda2a3524a0f84f2bb33871e969321e34c0af763e3076e7f460e92e6cc346eb967e087fe895a6acbbca4f7dd6b474ce3421a5c24f07129f17b389059945b911b318113acae2204d6152a672ffe6be7699486fb5feb674a9fa1d63454eb801e0b0071570f519abe7972bf439e8cf28cce87ba571e359329415b61dfd9605d2e5ee440603623c89bda7c629b4a2dba72f964bf3fb0a45d364198059f2bd2b10ffa4bb0a422889998c100754be16415e5a16450ec06bad480226610e78379eb5be4c2f04a91545a58c5916584ce39798d9f1dcf7742481e3fc90226b8a508e45a3179b207ca949a9e68369a4e7a0e259d4706e083f51f4172b24d00940a5e40c5041721c4e2ba1ee11f1bcf9c2ba0876dcd5bc3b74e779fe4fb8318c7406d47ba5b7da0d76ef4979ba783d83ebf8c7d834f6b0e244320263afd46395579b9a73d00a400646144e651a725f8d554dd3612862c70c98fac4a6dc4e7648ae1c99498221430bb83c7a9e1667315b09ff19d292693ca49c81463918d0c8f89e56e7f1cb588f12100b03aae55f7fbf686c6d8fcdfd2ec95fbde59c33ef6e9162c39088034e3427bfb892d72474b03a197db7242e68fce96b631564d2f94eed762c1c722e38040e22909f91577a6ec1e2c330e5a47c8bb5e6ef018f1a83388c73eb754ff32cfb0c4d38ba8b27885ddadf3f520c796d6fc499fcbc2a3c5a83b97cb77a5174d60d2c1234826405656d94521ee61b88a233af7754bd82be24ee44bef1648438d4fe075b2767664640965dd21e2f989bbf4f2f69c5b1d0d1ca61c90f1ccf62a2f766251a5ee4b01447113c913372d0d49d6415b7a479dc7ef064c184b9140d8bee6a0c1089dd993d092a9e654da9b9d13748d9ff552c1d284a9543151223275818ac1bd3d204262544f9b536b40ac64089e0ffe99370db2f5a5303b14f7f1b43feb901c4dbe086dbbdb2f83982b5a415dbd75c8eb212760574f15b9065a39654e17829f0ed29bce220f0eb2ba77754697a8babd74c5dc104896f4bef310d99a77532076a118a429672a66d8dd32e72a259623f4a330291c23a0b39b4376abd57c6b92ef5e2faffdcb6fdf0419185e0528d35366a1b865dc91dc4a1e228379b219eb52a47d3160a98e6bef560957039f8687bff28bbd1654b0e1a2290f335c53f0e0016dcb8f0a0c2e0d7bbccd7415da0fd8725055b26393b87f071918eda8414cbfc13d7d1c5beffe174876dd3ddae96fa6cdf87acad3ba1709dea8c80f4c7d2e4bf0e894e78471fa89f727f99c2073b57ad0ab2b23ab429995f9eb947fdb176e28c2ff432eba5830407d95888ac9843beee5f7c224ecc1a40afaf7534b5c3010aa00d026e74c47892b4c803ccba015b1341b380f9b441eb79f26c0b1c8447d23f912dfb9d2fe28a5cefa496ff3dbde6fc9fa6767339c8035ca97086d6aadb88b0dc1321688e887d98b25cd01819655e9b1432200c665a0a21d40e7e0244f1c373a202aecf5ee088442b4fd2c5313001a02f584d75eb7b6e52776bcfe474f016bd692a2062da16361e2626c90db4aee731753bac3d68a2963f02fab8f43d4fbf52461e1737999975f52f29533837f361e89088c3be55ca975278143f7bc986da19f71c483e99e599ac5ec9c532fbd2c9dab0e088b6978ed7e65dc278a736e5d03792040ddeb18cea5bb841cca78a2f449e0b360ab2ce1f1dd4c8cb39ea12679615111308ccef9632654f64e19d97906c95bebb106b2700a865a5165b9b8ff470a3b72fb6a7f3a91f58d47828e155cc94dcdd10ad68ba207859b696ccd40783fd81f814647a611ec4c8370bf8c01b1ecc618a945f5432f2981b1c4b34c75077d83d252d91e6be4bafe7db360df60b17cdc52871f581610fd0a35442b466230b85830d294f38eee8d3f464f1191bf6f3697840817fab5682593db32bd18c3e35c279a623cd32c6a3098f4ebd0c1b8944809175090b273ca8fcfc6b2381835cfef50ad7135204c539b5ef18e1256f2107d81daa0665d4e3bf06f0ecfc0a1cf514d0aab105a9eb9f695af73b3ba0df3cb2d1c46b7c02bf108567e6d01cf49e6d767d078d0f7eb40d460e34d78692e0114c3f260886fcc7e00c8b3c7008ff429d1a53aa9c2e2a41d1cb34e154057c4686b4ad1d704d5fdca331c9f8168d21ee75a74a465224f3327845653d52575e720a797cda03fef68668ce7623ef7cc9034bee94b8b0181f2b11ce4e8710e7497246d272de79509f4e2fe6c8b42422f866c5c69f6fd41d41c9c326912a2fc919382d4e3d414ef9fc9ddf546ab52c8d93afc585b420390737f26228865603a7ae33fc8e19c944994028eb2a20b57bfe1a2f15f0f372fd14ef2c297e78695908b0d6cedf144040f14908d59b7ffa58b1b1930bd65270942bd8043e38037bb5213ccccb00db3df8a661e6fd31d165ae465810350f8182320df72bd876d111321de34fe704b96862982f670d697336dcd782eee7b90ebac50dd31b62a91af03d377532a157b4262a7d1cba57fa045d75bda66dfc194c0b8d09ef91d887469dafcae262b0ce0c2d8876e189885ee793cc622d42f5719247341ca89e1cdd4026584e5087558fa70f750cb3e58f15f4655cb2aa5c2072ab82f11dd0b47a22d769197a373b5a799afb067f275a5477a2592432165fbe5e5fe93416f0816a98599b5cdb93b09175bb78f688bfa15eef313efb9d49047188736c9f096d86d047d951f18de0d5bb2b435d9cfaa88521fa55553ac1171aa3676e41ef1387921e9df4c7c6e996e54033a9115e1159e9cedc6121ec9e81a1f516dcde852f17605c68e6a41ce312832b731dee504377e669986a4438985af9523357a9f41ab6b9d08bd5e05aa971a009802ce32e31c29228a1f314a2da664067dae295662923d7715c95afb00e860d12d7b168ae050cf29e4bf1ca4fd61b9129794383fc25db22ad3229fe1d00f4761e37e897caa298944185e893a6bceeb54e22b467102f2d8e3e1b2aa2658687b4f52d165f059f4053284f6f3797ed016a1cd51d6ccd1920f19badf516351bb00b5ac3bf3f00da9600778734dfe59ff118b80c46662518e010c4e1220cc9a38032e0cfabd5a5a5da75a1a827179c567443a9f95c14710c03c74cfaf3ba71459a2e6c84aaa0c8dee4d69514795029ea15228a4696961201437035923818557408cdb632f2206a5d9b67d82fb00e9c7c62a3a47c330f5f0875884e7471a7c2cf9b2ed4d9573c90e0ddd52b583e9ef1b23884e89a0c0c801599f00ff79b96f561378e455584cde3a3f5a4c84a86cb62c0998f4f6beba9ff1d9cc5d092a623abf9cd026ca12b2c816c0a63ba7666422d2609f8896501f94116a4ffe86b44b683ba8071cdd9b7db1f7aa2a76e30c1607e8a6aa03a5384ed982b6b6e809ead534b62fd2963a7027800b08e53c94f16a1b00b753322db877ab20fd39eb60b9280409a4e963c6e5ef6f66e7f8713a7690762ad71158a9c3698d42fce32038142ee603c46b0d6510f279e56c2b05d7096c2416cc3dfba58fdc0c50a8b5d89048bef8ad00d7b6fc744234baefdbc29faf5e4694f0d0bc87ea2397a0de33752094611077ec049e3deae0062a3d54d9c55d2986f356ee54d7ec2645c18012c3dfccee4fed0bbf219d4cdd93b94c2f1dcdd964a7ccba4954033a07815248455ea2bc5b096f90cb81d4b534a82a8c55f8df025430884c395c2992128871e87c67a24158e7d9f2e700b685d9450e5084256931384cb517ce80ecbd955671b13c2855124079a3f8a423b00e0512ba3ee812ac85cc596ff81ccfa7c31dea015cfeea3704b8289741c066bf18d60a8f6e453921513d10a7494b7a8ed8bf4430e906fe2dca5b1f2daf50b5151e78872f7f971fde706bfc267af4d6c71c43c461b331c16f81c053dfc33bbe0973f91e979d96622bb2805eca8bd1ca1c86a7e4f2222e04ad38f0cee9892e7f9aec41ea9fa52334b246d476203f7c2d5f6575b7d126f845c8396208049ac014e067a49c7da865ae3e17c4b4630e63630abc4958e1924a21d32c26166f01ae2b8da72d7a56e1afa6621287663eb5d2983187964f81753ae5213daad6800140e34f3af2d7e227cd2c5bd9d4758586f6a4d60cd0cc70034d64e1fb4d2ab6ed9be9b2810571f2189149d9dfd08f1f8abe05475d3f445230413f3c2d8d7a32ff4c27a59f3922a9523e81ceb9a2b0a35482808b08de05f2686e7dd5fd0b5cf19516cb0558a76d4d97770e28404a2e78dcb63f3b96eb19f1bbf291e88a6145eb27e6fa5ed3609e829094f9cf937c58dd2bbeac045e27afa19407b425abe489f434d671692197d5cf72c07c8e2ac447b2db81b8e0e248d621f0521aeedf472184eff3527a64c13f0c601b3ec2048af2672255c14010fb1430fdd8b36f426c6469ce3c6b0be2cac2ae4ceea8baadd17bd0900a75114a14a370e499163de5a0e817ec703ec5b636140892fa8203d20c032cbd574fc34d2385320f8ef889048179132035bf18bc196f5b08bc5e4d0ae4bf18768342d5b48f90d27c5eda7c200bb025a2de774ef9a8dfd0d75fd844969950a13f5b4320a250d4a124fd0beae2313ac372d0583b79543c07fe4a31afc144acf99238c31b3e0b9c6241225467d7ef6a633313309fa84669b737cda0632d2dd36ec901e1ae3e4a68210fa0b0fa02588eff6e72f73f015951e42f85868c622a8be41f3247a2140f3fc0a30d31f2379fd387240e5f306a17b035720f57dba876e1cd0d33bc87d966d620e65736370fc817ac4ff15db12147cc85c230ebef736047408e03ca8f0be6a642030b5627458aa1e65dd56737ab810b2a8b492586c42e193ce28d9aae97a13db0cc36d358f6efdfc379ee394140a6700fd99e86b8f7d3a594cceb4bbefff68ce15866cdfc9894097f08e89b66f8c30bffcc001044b0594922e11b8a055c9f6ee026ae31ac020370a1f3e06daa5a772272941a101c11714fe8d8e37d40c3122c61d8f610d194cf82da8419cfa1680eb14ee96b6686e9f209a00df36e96a9d6f842571b23c753c5577e9ed466bb585e8910722050a93a8463bb655ccac5d9900a31a460738ff88a624304a1e5b37fb9ffb23887000c006db10e2b4b8985770d88102ff55d36e7513f37acb4c5920fbd220fc20c8b9b1a83128b0c64bcf26f761bc01407ff23a9b11942b6034278ae1b4817169180989eef86eba3f781c6ed7c82b279089823170533c0791d57ca17f1beb9bd6fda0242993bf5be154fa2377340d0c0c8fcd0da22a25000793deaaf634421431ac4d940c6ccbe9265f52eafaa6660cffe9ce6f0ae13c38cb7ce5304e697ff3814a992c030ff909d9b33615c8f9be2f199990be4b96c115324f1f53c609d9c70c9a3d643973be4f407aa54cfcb7741124c48570a6a3906b2eaa017d1c4e98b1336932976e7ac4b2cc3ff548a3d5272ca9b66092c0be62c903cc5f6022fe892c37fbaaa5bba75f41e40b410b223b052ed39b580fcc936b9582cf7c479ce148f56a374df66ddb07dd6547ed67da09e72e7820676bd33cc3dcc778b5e5929677908c6f43d3f2e793b768cb2bb6c11cac258438c7630aa10ec88ac11e66c0855e0be63beebbc4a846a4210d3b16a1a45b07144aa8b8587d6d981683640bcdb7390599cc6b806869d01e425cc59f78d26e009412daa789b0872610de5d576f928321c80f83d64dd5cac4562cfbd72b56bd17165c7c60feb78c973289e4afbb58225262f916b2e189f9195aaaaef30f037d1a0b47a5e1ad425f16c581f4317b4a1cce0458eb64e01a2ce88067b6365f20570e7a6ef374c26e7df9d9b86f1a2ed3f3a6f151d108f24245346f7fbacc41e39d1b89bbc83ce93442bd733e4fb70c090135e71518d07f6f7cb5484256d973b5e107cadc46b3caecb4a0c85930d59631ae69c164b9b51d4af12b414da442ca6e2e2e905b3d35c848bc74803d93cdd1a52642f101ecb76adfbb324aab602cda06f4142f88ea187aa2d9bc2770fb55738bf1c22a0172dd3aaaf0756da7919ea8fd480bd1852cd97487ce18b5406f1e208103de94a56189a902b0e677d3ebb09b9b4403e78dbd0aa42f5e177f3c0e44bff9304af9cfc15d61d2b7172aaff7db8e31c401014a1d5637a540050a7c24f8ce92509be63c99f33c3e169be4e48c5c46c0d00536d73fdfaacd543fc05f62e194e64df3916521560c2e7c70651b0c8cd1711e13cf10ed6d7db7642d832dac4b6699b200a8bae01e5c271a7cb1dd55be7593252ec16d138fea76b7ab76eb2597f4847ffa61f1e5560f85d5b1dfb976384bd384137d0d599a996035b696364b2bbd910add9dd520e1d50d4acbaa28865a9eceb70caf6cfd4e46b787fcef171be5316b41061ad0f844d78c571948c0c3b9ca5b581e66da9006818668cb3024873fb6c209e388964e71e4e7481f60e98293614a491c4343db492b43eaf5acc1b24720b645f73a741b8082ec8e7f0940270bd337222e0e5f38ae454a0bbbb97e2684eb4c0f8665e2f5fb567a7fc8f46d42d76a87b9167d5caaa34cac8c6c35e92f339daa280b074923950ca4fbe26d12fc08df611b122b294ee8165d619809c84a8483ac031a91a321ba578cfff08ff1da3638da881a658ee5025ca8cd4c2412bd64fec5acce4a063c903a928a1bbe47a19d6f20ce2a45e48b775e956e7328e2807bdf0699717624dcc4bea3f49696076d6bd1f3b32648c84b627afc98ee7a255a9b6a7688bc2cb1d022b456c914d73c31a61cdf1f0990d46e50809e625fee7f66f13f3fd3d711053420474e2137299fb499c55d280fadb264bfa1c01fb103c85e1025144416fb45c410b1de94ce3b6b39e31dbdd40085c18596d5a4f3aa5d72f5d762f32657466c3d6cfa119e4b15d66c166f9235130b6b5db7bdeb688674c2c9ffe5953f913dc8b70c89ee3a3e9d0b900d3eb11b0c998e9028b8af68a3a123f0a5aa56f987cb287bed1b4ec7ea4d5080f7460191140fbbc521c4b4ca284d2f52ba94d12c57cffdf6b3a1823c8bec3f227f0964d0269567969b2a6952d67b2cf3fe4ed64ad32021de8df302d6fa0e5934dcb0fe0207c4eaf81924185e0ae50fa740e900c098f17483a8c8e9acbd142b0d3b8d7ed31222b7d9d9642133a4d710850b29be1b312f2043e0553468a03a468da16119a0147bc6655daaeeaccef77df33300f0304b229d0a67f2e2686ed0faf697a8c0be02a92c5eecec61f86730c46d3e4060cedea01e6ad69edab787224daf217fc00d25bc51eaadc89bea9ed8b8b064d00a603bba923afd50feeaa28480fe0bf5208e1f6c3c1ab06c7581870abe1d4263967bca1c83e6d829d9967b15b03c71662b051fef32423b6f60529ef22b34e79fd39a03188920fb378c832b94ae04ee9c1e611c19e994f3fa9a123454d8453ff1ab96e3dfaf32adcce9ce3a58d3ab46800283017c41df169d9030faa8635070293a6b4f92c2a03a23f322f9219eea5c13a65e16f8cfbb4c12898a9b5666c005ee1bfaa0d64396e1ffb0c89394ac75ce404e341592c70ec8ef696ac200a491dc290b060b0d8ab0b0d73a785117cb992fd5082920edf8a802617e40021efd904982120cb763677cfbf5fb26e040250ff71a9e8f0a74f8c8c0cbb2d83b70592dcad430759e5e3365da322b45198c34067f1ef09108461a7aff586e522eff5dd06ba39f32adbbb2ac11cb5ed218b080ef6b47ab0a21d7658d2c57a0b782547f8c5acce7c340cbc4e32291a4f4d261d51eb5d19fc313f13da04d155cee22bdda2a05328dfad96d058d2752e41372178a8ac3069d27277f5ad615312c8a0da2d6f5967a6191fdfa66a465ba370e3d8a8b27f31b75cc1d1dfc9e914df09e8044c0d753950f5a662bdec0dc6b50c3fac197df947b2fe927b02824b638eba85cc6b3a3a4633ea595e9e7c01764122a2a188c7f05b86a0901781fe9cf000e194128b474d8334de7204e7cf9ba4c45a82851cc873cb2d851257d3c067a363b22d3ffc4592989e1cdd50333e7d20858eaded8fa02b85209c329cb4ba3f99922719ac16e761fee300db4c4022070de18149af7d30df3be006208f5703c677000acfdbdab00aa099e0c515cdd6c36e4c2fcb14d4a169ac5226206d780418161648be96d949099a7cd18b491d72cb00baf6a36552c49578cb858004493fa5ffabec5316a34f8af4e3e329c28ace755c8329add179dd15a716fc30cae70fd816fb9ae67f0e595a39585efe683dac6569cecd5b4612600ca7e58142774ac04ad50ebf3fd3ffbe32cfdacc60c4942daa5b1dbd52cb4570ce3acffe92365dd10cb6526daa7ba1a0b8635167f7220c97ce29b11fff0e2780c3d43d82700b6511664384bafbc8ace53cb17529a7cbe4fa1e5b8242a149af5120092d5f50af044e091387602eb00690a21ed41b2b6cd2ea58ffd23c273f07932cea2d0e81664d0b1770c1a8726cbf09e109bb62cf0bbd05bd068eeb95e2f120470b4082e0f037bed194d5aaedd71fdb36b71ebabfc29ba3fc10086cf780c1cdc74ebe0250fa517cbde50830063cefcc39e7848ae2f4ca899903296fefa908e8c18e1bb8a1437ac612ce96cf6c0617a6cd6c6e8a631315a1d3e29d803b558c3a4274960d005b212c9c31e65649812c0dc066ae224f5001a4893fdad31cf49271b8df0353c2feb0c6796198d5ddff04951416833ce4200813f0f39d2ce75da2985d4329c1f2b2023d34a62804670f522d449b1edbf12c255673077c821683b1d8d7e5ddf800a4b7b539cba5dcaf1fdb336daec57ba5cc8e484c04b5e70f74ee3a0a2e954e2175117e97ac2c5bdc2dd375946a0355561c2cb8edb457d5185a93189e13a4aea066c51edf64ec0704d127db99b2ce23ca840435bc4ce9d8b533e23bd09c80f5004b23d8666c77a078368ad040c4e1ce3d4fddf9ed1b1863c9ed1d2eea60ed85f24edbcea0b513edb75f6321e428db3b3082154a7923b2776b96f9276f251b155188c9415d7e4f09e37f89fbaddef8410979efa36c6a1efe5565349a96498aea518ac498adad8a301e7d171c35883c89798ca890032253d77b00393525dfc1c992b73dcd9fdf3f304f8b5c1f7a71f4036d4095ffca43ba1c2ed25761063f0ee3925fcb36659ca28e692b6f5e89599405f990c57ec7237311d663bdafde7d2f81d0dcc1c522e2497eb105f9d9bb4f969cb1c89c2c4010dead0a19023f4bf15879c2f13afbd1caf80f6e3b5628c1e36f57189ec2f5a61d03eaa58e379df3027da6f24403ef8d186c62c5176573cdb2e799c65bb6c0da4fcaba21d4b8c02afeb6b8717fe4b74f45e45025aaacd2ee55901d81ab63d04853f222695e0969e3a9f89b9297aa44364a7a2bcd0be5759f3a27bc00d7a99f32b69cce48cbba3cc858a9fc708deb0b9515755e48edb793a598de521bfecb827ca3fe1e58b9875426b76a268137ba4e5fe24a5d7f977fe9d1b50ce573fe98be883ce057ff046e26d999403f52ff08e8a7dfb7f993a389212ea2ed1bf7725f4a265bdbfc208fcda3df33465c90928f1e1bb77db1955ef73583031893947c902eecff3fe24903319d5c2433e0b0546305540bd96a71fda765fcad36af678a4f07cc95fcd02e81ac2760931d40c132c998a8c0b120abd2d3e666518d1ef5ffa130cca13b78e57cad7a151bb594a1e394e73d71aea3c2d6e5cbbff872c98714fb5fb571e1e3743eea704b295666d21ce4e84ca11a1d673a27b169c5f83088e0ed344fd75dd4b741b05226ab93513305f4a7f83ded532c62d697e04ba2a963e30c321f916c0080e9fbc5ab619aa8c220f38de69ea6d5a49d99f904ace4f9c6132a8708e225fa2ab197c4508ff25c42a92d2a7cc3855cd59f6c96407266922cfb6f523d46e47e29d0447f32582e1951a6b0361e26226f8379a50f2d5e3204c6828da40b8e7cdc816f6b166ef4aa723077f7be16b732f1edab20a5668e50844b717dacc74dd94451cb9460cca88ad8a22ff47083c488ffc04e3b9c39ec7b25f41ee2a2a6cae755bc5d1fb9e1633549013968d569bb6c6f0d95547d93eba03a1da40766f3ab3b18f19b30964af1d69db15b13c2024eb855dd70af579f46fad36a77a77c2cf65812e92125f2f9e4011eb2ccf4716ddca655668da9838e29677662101824afbe875bd3490fa1a7a286ae31704b40587eec531af8bf89a26692686387927daec1d6e004fe9105160219a7d23035a362fa532f57b6269292058de35d8cd36c4c6b1143821adbe16379d11e1232646f4d497651f0954ea9a1df894dfc491003980f60b52cc0a1ab26756077227ee3102ea57dce3271965fbc8bd6e66a68a2b9543b577278024cd4536b2741226c3b1d409828f4e0d9874ecd7069da3c9c77f290915adcdf6b456983796b3fc28042c760483b93e07a9ac85a3991713d1c2f85b94556c6d1a273da1ee90265d01a8981245c7f7f8d10b2915126d9f9ac61a2d6a8c055ea87be9bafcb5643c9b119120d465b5ddb005f7ccba998fd30058ffa33d82c9d8e4e301e780bd7f93832604cfea9de23f1d6b2a50f65047ac9f879eff0451f311164941d680b76395aa208016ec3c5dfd1e4e2451ec13fb58f2242b1c26e54bbd88b44e163e4e7c7aad0b3d5897b5f0d8d49522bd7b5c1aeabc1faa5f92051b7c9609bf3dabb085a533130bb3f740556f8e71453758b79613a124d615ab01b2e1de8a4e519947fcd62b059aca90452fd3b086e6f37ec6b9c8c565885d7bc2125f2a6edc128992c986ed1e16fd93e88a03407f497ac742f49836ac0886ff6354b8a0d7096dd464cbbd9e1182fbc13662c46028cf859e93de493b32a8060828596b2fc4245655a4f9589c0c3e6a3130d7703084bca793e15a9e9d6366f1898695e5d47b7da2fe660461450c47a7186e240f9f600739c2fa2038f28aa34075563b42e39189fff917f77b1d169abf6085508100e18e9651f68113f0371c993ba004d5ac582763655c4df79c28668406aa60c8966a70355446b5ef97eca8f9c21cd311efbd65d6701932662d214ba804cb02a5d0c4e936c50435a6d8a2678558c26aedd38694ee726b9fd89a039a0378b17166714bb1379bb173d2e916411278d8285e9e6dc901a616904b1927280ff562b631da5d3cc81ff70e3269a358e8c4dca3696ccdb546ae1147014621b472511388d61cb1e60590ad87f05617f122417a4282d94bbc643f0547a6682008695507d53505be0bd9b78ed3c7adb01360df7cc3b4e8ac5357485facb49c2e0600e9a923c71d12fcaffcddc8de640403018a9a2ff1c2ebcdfd6700facbbb2dbc42db37fa4e0b64fa45559ed421cf9e91bbb8533983393113a75023ebc8090bb50f8d0b75903c3c8e4b51137f3ccf2b5ee42e549fa6c2c57a336718e1867420e656c75e985702a0b1d78e451a4d0e7abfe681ecfd92e1fe5d00c00a22acdd0ff415858ee7fa9d195fbd1071d672106427bcd2c8f5114e6b4e3b8f6fba28c62b38318cbd8088a2b956ddcdebbcfe0e6670d37b63276fa30a16791a3fee1b9d", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4411dfdc7cb6265f100524012f038ee1f205bf8789b70b46c57463dedb4998f7ac800000000000000000000000000000000c6895e67e144556602cbe13943c8169600000000000000000000000000000000808b3aef60f3e74a629e9331d68a14b21806572c19ee2f3532e970d617919b3aa8cdc582782dfad0699badc631f75128000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022c17de150116bdca2a3edbbac4f77678c67c35f056f9e969387b99b5c26da55509a23e762ede2ab19d289955f6fc34a289c4196b6f4fed818bca760158098e851b1e385ae72fb3add3fe838e63d0e080c2409bd2fe5f57f926a01a4e31278a1e0fd8fef8ee3dacae5dd4bd8666f29088e7e2c1680f50451cf263d3e53ba2b8b4000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } +} diff --git a/circuits/benchmarks/results_insecure_agg/report.md b/circuits/benchmarks/results_insecure_agg/report.md new file mode 100644 index 0000000000..5913ec05d7 --- /dev/null +++ b/circuits/benchmarks/results_insecure_agg/report.md @@ -0,0 +1,214 @@ +# Enclave ZK Circuit Benchmarks + +**Generated:** 2026-05-23 11:19:46 UTC + +**Git Branch:** `feat/1549` +**Git Commit:** `8a841717468169df094b7316e57e1008d4ba34b0` + +**Committee Size:** `H=3`, `N=3`, `T=1` + +## Run configuration + +Settings for this benchmark run (integration test + Nargo circuit benches on the same host). + +### Integration test (`test_trbfv_actor`) + +| Setting | Value | +| ----------------------------------------------------- | -------------------------------------------- | +| Benchmark mode | `insecure` | +| BFV preset (artifacts) | `insecure-512` | +| BFV preset (enum) | `InsecureThreshold512` | +| λ (smudging / error) | 2 | +| Nodes spawned (builder) | 20 | +| Network model | `in_process_bus` | +| Testmode harness | true | +| `proof_aggregation_enabled` | true | +| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | +| Rayon worker threads | 13 | +| CPU cores (host) | 14 | +| `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | +| Verbose logging (`run_benchmarks.sh --verbose`) | true | + +### Hardware & software (Nargo / Barretenberg host) + +| | | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **CPU** | Apple M4 Pro | +| **CPU cores** | 14 | +| **RAM** | 48.00 GB | +| **OS** | Darwin | +| **Architecture** | arm64 | +| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | +| **Barretenberg** | 3.0.0-nightly.20260102 | + +--- + +## Audit status + +On-chain verify gas: **complete** (CRISP Π_user + Enclave Π_DKG / Π_dec replay). + +--- + +## Measurement methodology + +| Metric kind | Source | Meaning | Do **not** use for | +| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | +| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | +| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | +| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | + +**Harness limits (integration):** all ciphernodes share one process and bus +(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare +runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, +commit, and hardware. + +--- + +## Protocol Summary + +### Circuit Benchmarks (isolated Nargo + Barretenberg) + +Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). + +| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | +| -------------------- | ----------- | --------- | ----------- | ---------- | +| C0 | 6847 | 0.12 | 26.22 | 15.88 | +| C1 | 57818 | 0.34 | 25.94 | 15.88 | +| C2a | 41244 | 0.31 | 25.78 | 15.88 | +| C2b | 79591 | 0.49 | 26.23 | 15.88 | +| C3a | 120114 | 0.57 | 26.46 | 15.88 | +| C3b | 120114 | 0.57 | 26.46 | 15.88 | +| C4a | 67494 | 0.46 | 26.53 | 15.88 | +| C4b | 67494 | 0.46 | 26.53 | 15.88 | +| C5 | 123624 | 0.55 | 26.63 | 15.88 | +| user_data_encryption | 53732 | 0.33 | 25.67 | 15.88 | +| C6 | 86927 | 0.53 | 27.16 | 15.88 | +| C7 | 90841 | 0.46 | 26.43 | 15.88 | + +### Artifacts + +| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | +| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | +| Π_DKG | 10.69 KB | 0.47 KB | 3125294 | 176148 | 3301442 | +| Π_user | 15.88 KB | 0.12 KB | 2972881 | 170272 | 3143153 | +| Π_dec | 10.69 KB | 3.47 KB | 3640985 | 187260 | 3828245 | + +### Role / Phase / Activity + +| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | +| --------------- | ----- | ----------------------------------------- | -------------- | -------- | ---------- | --------- | +| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 144.27 s | 127.00 KB | 128.19 KB | +| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 131.81 s | 10.69 KB | 11.16 KB | +| User | P3 | per user input | isolated_nargo | 0.65 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 0.53 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 53.36 s | 10.69 KB | 14.16 KB | +| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 49.52 s | 10.69 KB | 14.16 KB | + +_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **20.40 s** — not +comparable to P2 wall_clock row above._ + +## Integration test (`test_trbfv_actor`) + +### End-to-end phase timings (integration test) + +| Phase | Metric | Duration (s) | +| ------------------------------------------------------------------ | ------------ | ------------ | +| Starting trbfv actor test | `wall_clock` | 0.00 | +| Setup completed | `wall_clock` | 2.66 | +| Committee Setup Completed | `wall_clock` | 20.17 | +| Committee Finalization Complete | `wall_clock` | 0.00 | +| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 131.81 | +| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 144.27 | +| E3Request -> PublicKeyAggregated | `wall_clock` | 144.78 | +| Application CT Gen | `wall_clock` | 0.01 | +| Running FHE Application | `wall_clock` | 0.00 | +| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 49.52 | +| Ciphertext published -> PlaintextAggregated | `wall_clock` | 53.36 | +| Entire Test | `wall_clock` | 220.99 | + +### Multithread job timings (`tracked_job_wall`) + +| Name | Avg (s) | Runs | Total (s) | +| ----------------------------- | ------- | ---- | --------- | +| CalculateDecryptionKey | 0.01 | 3 | 0.02 | +| CalculateDecryptionShare | 0.02 | 3 | 0.06 | +| CalculateThresholdDecryption | 0.02 | 1 | 0.02 | +| GenEsiSss | 0.01 | 3 | 0.02 | +| GenPkShareAndSkSss | 0.01 | 3 | 0.03 | +| NodeDkgFold/c2ab_fold | 8.17 | 3 | 24.52 | +| NodeDkgFold/c3a_fold | 35.19 | 3 | 105.58 | +| NodeDkgFold/c3ab_fold | 7.49 | 3 | 22.47 | +| NodeDkgFold/c3b_fold | 34.99 | 3 | 104.97 | +| NodeDkgFold/c4ab_fold | 7.89 | 3 | 23.68 | +| NodeDkgFold/node_fold | 18.33 | 3 | 55.00 | +| ZkDecryptedSharesAggregation | 1.56 | 1 | 1.56 | +| ZkDecryptionAggregation | 47.95 | 1 | 47.95 | +| ZkDkgAggregation | 19.77 | 1 | 19.77 | +| ZkDkgShareDecryption | 1.23 | 6 | 7.38 | +| ZkNodeDkgFold | 112.07 | 3 | 336.22 | +| ZkPkAggregation | 0.63 | 1 | 0.63 | +| ZkPkBfv | 0.22 | 3 | 0.66 | +| ZkPkGeneration | 2.36 | 3 | 7.09 | +| ZkShareComputation | 2.39 | 6 | 14.34 | +| ZkShareEncryption | 4.00 | 24 | 95.92 | +| ZkThresholdShareDecryption | 3.39 | 3 | 10.18 | +| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.31 | +| ZkVerifyShareProofs | 0.27 | 5 | 1.37 | + +Sum of tracked job wall time: **879.74 s** — **not** end-to-end latency (jobs run in parallel up to +`BENCHMARK_MULTITHREAD_JOBS`). + +### NodeDkgFold sub-steps (`tracked_job_wall`, per fold prove) + +| Step | Avg (s) | Runs | Total (s) | +| --------- | ------- | ---- | --------- | +| c2ab_fold | 8.17 | 3 | 24.52 | +| c3a_fold | 35.19 | 3 | 105.58 | +| c3ab_fold | 7.49 | 3 | 22.47 | +| c3b_fold | 34.99 | 3 | 104.97 | +| c4ab_fold | 7.89 | 3 | 23.68 | +| node_fold | 18.33 | 3 | 55.00 | + +### Aggregation jobs (`tracked_job_wall`) + +| Operation | Avg (s) | Runs | Total (s) | +| ---------------------------- | ------- | ---- | --------- | +| ZkDecryptedSharesAggregation | 1.56 | 1 | 1.56 | +| ZkDecryptionAggregation | 47.95 | 1 | 47.95 | +| ZkDkgAggregation | 19.77 | 1 | 19.77 | +| ZkNodeDkgFold | 112.07 | 3 | 336.22 | +| ZkPkAggregation | 0.63 | 1 | 0.63 | + +Sum of aggregation job tracked time: **406.13 s** (parallel CPU work; not P1/P2 wall clock). + +### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas) + +| Artifact | Proof (bytes) | Public inputs (bytes) | +| --------------------- | ------------- | --------------------- | +| dkg_aggregator | 10944 | 480 | +| decryption_aggregator | 10944 | 3552 | + +## Raw circuit benchmark JSON (Nargo) + +Source files for the **Circuit Benchmarks** table. Persist this directory with +`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without +re-running the integration test. + +| File | +| ----------------------------------------------------- | +| `dkg_e_sm_share_computation_default.json` | +| `dkg_pk_default.json` | +| `dkg_share_decryption_default.json` | +| `dkg_share_encryption_default.json` | +| `dkg_sk_share_computation_default.json` | +| `threshold_decrypted_shares_aggregation_default.json` | +| `threshold_pk_aggregation_default.json` | +| `threshold_pk_generation_default.json` | +| `threshold_share_decryption_default.json` | +| `threshold_user_data_encryption_ct0_default.json` | +| `threshold_user_data_encryption_ct1_default.json` | + +## Notes + +- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is + effectively 0. diff --git a/circuits/benchmarks/results_insecure_no_agg/benchmark_run_meta.json b/circuits/benchmarks/results_insecure_no_agg/benchmark_run_meta.json new file mode 100644 index 0000000000..ac9e71558b --- /dev/null +++ b/circuits/benchmarks/results_insecure_no_agg/benchmark_run_meta.json @@ -0,0 +1,11 @@ +{ + "benchmark_mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "proof_aggregation": false, + "multithread_jobs": 13, + "verbose": true, + "nodes_spawned": 20, + "committee_size_n": 3, + "network_model": "in_process_bus", + "testmode_harness": true +} diff --git a/circuits/benchmarks/results_insecure_no_agg/crisp_verify_gas.json b/circuits/benchmarks/results_insecure_no_agg/crisp_verify_gas.json new file mode 100644 index 0000000000..af8f2834ef --- /dev/null +++ b/circuits/benchmarks/results_insecure_no_agg/crisp_verify_gas.json @@ -0,0 +1,88 @@ +{ + "verify_gas": { + "dkg": null, + "user": null, + "dec": null + }, + "source": "folded_proof_export_plus_crisp_verify_test", + "artifact_sizes_bytes": { + "dkg": { + "proof": 0, + "public_inputs": 0 + }, + "dec": { + "proof": 0, + "public_inputs": 0 + } + }, + "calldata_gas": { + "dkg": { + "proof": 0, + "public_inputs": 0, + "total": 0 + }, + "dec": { + "proof": 0, + "public_inputs": 0, + "total": 0 + } + }, + "integration_summary": { + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "bfv_preset": "InsecureThreshold512", + "lambda": 2, + "proof_aggregation_enabled": false, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": false, + "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, + "operation_timings": [ + { "name": "CalculateDecryptionKey", "avg_seconds": 0.119593152, "runs": 3, "total_seconds": 0.358779458 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.625714819, "runs": 3, "total_seconds": 1.877144457 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.555039542, "runs": 1, "total_seconds": 0.555039542 }, + { "name": "GenEsiSss", "avg_seconds": 0.126707139, "runs": 3, "total_seconds": 0.380121417 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.256695819, "runs": 3, "total_seconds": 0.770087459 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.225412791, "runs": 1, "total_seconds": 8.225412791 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 2.350571951, "runs": 6, "total_seconds": 14.103431707 }, + { "name": "ZkPkAggregation", "avg_seconds": 1.971270875, "runs": 1, "total_seconds": 1.971270875 }, + { "name": "ZkPkBfv", "avg_seconds": 0.429601166, "runs": 3, "total_seconds": 1.2888035 }, + { "name": "ZkPkGeneration", "avg_seconds": 2.953823722, "runs": 3, "total_seconds": 8.861471166 }, + { "name": "ZkShareComputation", "avg_seconds": 3.007209458, "runs": 6, "total_seconds": 18.04325675 }, + { "name": "ZkShareEncryption", "avg_seconds": 5.763399979, "runs": 24, "total_seconds": 138.321599497 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 7.663020986, "runs": 3, "total_seconds": 22.989062958 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.102607666, "runs": 3, "total_seconds": 0.307823 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.257090858, "runs": 5, "total_seconds": 1.285454293 } + ], + "operation_timings_total_seconds": 219.33875887, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, + { "label": "Setup completed", "seconds": 3.058201042, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 20.257082542, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.005129541, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 1.981342, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 22.251965458, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 24.866107292, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 0.309383, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.003993125, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 8.253253, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 18.401028833, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 66.904175584, "metric": "wall_clock" } + ], + "folded_artifacts": null + }, + "test_exit_code": { + "crisp": 1, + "folded_export": 0, + "enclave_contracts": 0 + } +} diff --git a/circuits/benchmarks/results_insecure_no_agg/integration_summary.json b/circuits/benchmarks/results_insecure_no_agg/integration_summary.json new file mode 100644 index 0000000000..73e312b4f1 --- /dev/null +++ b/circuits/benchmarks/results_insecure_no_agg/integration_summary.json @@ -0,0 +1,180 @@ +{ + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "bfv_preset": "InsecureThreshold512", + "lambda": 2, + "proof_aggregation_enabled": false, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": false, + "multithread": { + "rayon_threads": 13, + "max_simultaneous_rayon_tasks": 13, + "cores_available": 14 + }, + "operation_timings": [ + { + "name": "CalculateDecryptionKey", + "avg_seconds": 0.119593152, + "runs": 3, + "total_seconds": 0.358779458 + }, + { + "name": "CalculateDecryptionShare", + "avg_seconds": 0.625714819, + "runs": 3, + "total_seconds": 1.877144457 + }, + { + "name": "CalculateThresholdDecryption", + "avg_seconds": 0.555039542, + "runs": 1, + "total_seconds": 0.555039542 + }, + { + "name": "GenEsiSss", + "avg_seconds": 0.126707139, + "runs": 3, + "total_seconds": 0.380121417 + }, + { + "name": "GenPkShareAndSkSss", + "avg_seconds": 0.256695819, + "runs": 3, + "total_seconds": 0.770087459 + }, + { + "name": "ZkDecryptedSharesAggregation", + "avg_seconds": 8.225412791, + "runs": 1, + "total_seconds": 8.225412791 + }, + { + "name": "ZkDkgShareDecryption", + "avg_seconds": 2.350571951, + "runs": 6, + "total_seconds": 14.103431707 + }, + { + "name": "ZkPkAggregation", + "avg_seconds": 1.971270875, + "runs": 1, + "total_seconds": 1.971270875 + }, + { + "name": "ZkPkBfv", + "avg_seconds": 0.429601166, + "runs": 3, + "total_seconds": 1.2888035 + }, + { + "name": "ZkPkGeneration", + "avg_seconds": 2.953823722, + "runs": 3, + "total_seconds": 8.861471166 + }, + { + "name": "ZkShareComputation", + "avg_seconds": 3.007209458, + "runs": 6, + "total_seconds": 18.04325675 + }, + { + "name": "ZkShareEncryption", + "avg_seconds": 5.763399979, + "runs": 24, + "total_seconds": 138.321599497 + }, + { + "name": "ZkThresholdShareDecryption", + "avg_seconds": 7.663020986, + "runs": 3, + "total_seconds": 22.989062958 + }, + { + "name": "ZkVerifyShareDecryptionProofs", + "avg_seconds": 0.102607666, + "runs": 3, + "total_seconds": 0.307823 + }, + { + "name": "ZkVerifyShareProofs", + "avg_seconds": 0.257090858, + "runs": 5, + "total_seconds": 1.285454293 + } + ], + "operation_timings_total_seconds": 219.33875887, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { + "label": "Starting trbfv actor test", + "seconds": 0e-9, + "metric": "wall_clock" + }, + { + "label": "Setup completed", + "seconds": 3.058201042, + "metric": "wall_clock" + }, + { + "label": "Committee Setup Completed", + "seconds": 20.257082542, + "metric": "wall_clock" + }, + { + "label": "Committee Finalization Complete", + "seconds": 0.005129541, + "metric": "wall_clock" + }, + { + "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", + "seconds": 1.981342, + "metric": "wall_clock" + }, + { + "label": "ThresholdShares -> PublicKeyAggregated", + "seconds": 22.251965458, + "metric": "wall_clock" + }, + { + "label": "E3Request -> PublicKeyAggregated", + "seconds": 24.866107292, + "metric": "wall_clock" + }, + { + "label": "Application CT Gen", + "seconds": 0.309383, + "metric": "wall_clock" + }, + { + "label": "Running FHE Application", + "seconds": 0.003993125, + "metric": "wall_clock" + }, + { + "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", + "seconds": 8.253253, + "metric": "wall_clock" + }, + { + "label": "Ciphertext published -> PlaintextAggregated", + "seconds": 18.401028833, + "metric": "wall_clock" + }, + { + "label": "Entire Test", + "seconds": 66.904175584, + "metric": "wall_clock" + } + ], + "folded_artifacts": null +} diff --git a/circuits/benchmarks/results_insecure_no_agg/report.md b/circuits/benchmarks/results_insecure_no_agg/report.md new file mode 100644 index 0000000000..37d834f75b --- /dev/null +++ b/circuits/benchmarks/results_insecure_no_agg/report.md @@ -0,0 +1,189 @@ +# Enclave ZK Circuit Benchmarks + +**Generated:** 2026-05-23 10:09:34 UTC + +**Git Branch:** `feat/1549` +**Git Commit:** `8a841717468169df094b7316e57e1008d4ba34b0` + +**Committee Size:** `H=3`, `N=3`, `T=1` + +## Run configuration + +Settings for this benchmark run (integration test + Nargo circuit benches on the same host). + +### Integration test (`test_trbfv_actor`) + +| Setting | Value | +| ----------------------------------------------------- | ------------------------------------ | +| Benchmark mode | `insecure` | +| BFV preset (artifacts) | `insecure-512` | +| BFV preset (enum) | `InsecureThreshold512` | +| λ (smudging / error) | 2 | +| Nodes spawned (builder) | 20 | +| Network model | `in_process_bus` | +| Testmode harness | true | +| `proof_aggregation_enabled` | false | +| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | +| Rayon worker threads | 13 | +| CPU cores (host) | 14 | +| `dkg_fold_attestation_verifier` | _(disabled — proof aggregation off)_ | +| Verbose logging (`run_benchmarks.sh --verbose`) | true | + +### Hardware & software (Nargo / Barretenberg host) + +| | | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **CPU** | Apple M4 Pro | +| **CPU cores** | 14 | +| **RAM** | 48.00 GB | +| **OS** | Darwin | +| **Architecture** | arm64 | +| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | +| **Barretenberg** | 3.0.0-nightly.20260102 | + +--- + +## Audit status + +> **Incomplete on-chain verify gas:** 3 of 3 artifact verify-gas values are **N/A**. Re-run +> `./run_benchmarks.sh` and ensure `extract_crisp_verify_gas.sh` completes (CRISP test + +> `test_trbfv_actor` + EVM replay). Calldata gas alone is not sufficient for audit sign-off. + +--- + +## Measurement methodology + +| Metric kind | Source | Meaning | Do **not** use for | +| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | +| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | +| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | +| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | + +**Harness limits (integration):** all ciphernodes share one process and bus +(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare +runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, +commit, and hardware. + +--- + +## Protocol Summary + +### Circuit Benchmarks (isolated Nargo + Barretenberg) + +Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). + +| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | +| -------------------- | ----------- | --------- | ----------- | ---------- | +| C0 | 6847 | 0.13 | 26.12 | 15.88 | +| C1 | 57818 | 0.34 | 27.04 | 15.88 | +| C2a | 41244 | 0.32 | 27.34 | 15.88 | +| C2b | 79591 | 0.52 | 27.21 | 15.88 | +| C3a | 120114 | 0.58 | 26.59 | 15.88 | +| C3b | 120114 | 0.58 | 26.59 | 15.88 | +| C4a | 67494 | 0.46 | 26.91 | 15.88 | +| C4b | 67494 | 0.46 | 26.91 | 15.88 | +| C5 | 123624 | 0.57 | 25.66 | 15.88 | +| user_data_encryption | 53732 | 0.34 | 27.20 | 15.88 | +| C6 | 86927 | 0.53 | 26.33 | 15.88 | +| C7 | 90841 | 0.49 | 26.71 | 15.88 | + +### Artifacts + +| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | +| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | +| Π_DKG | 15.88 KB | 0.12 KB | N/A | 175000 | N/A | +| Π_user | 15.88 KB | 0.12 KB | N/A | 170320 | N/A | +| Π_dec | 15.88 KB | 3.25 KB | N/A | 188232 | N/A | + +### Role / Phase / Activity + +| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | +| --------------- | ----- | ----------------------------------------- | -------------- | -------- | ---------- | --------- | +| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 29.42 s | 127.00 KB | 128.19 KB | +| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 2.13 s | 15.88 KB | 16.00 KB | +| User | P3 | per user input | isolated_nargo | 0.67 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 0.53 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 18.68 s | 15.88 KB | 19.12 KB | +| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 8.47 s | 15.88 KB | 19.12 KB | + +_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **2.12 s** — not +comparable to P2 wall_clock row above._ + +## Integration test (`test_trbfv_actor`) + +### End-to-end phase timings (integration test) + +| Phase | Metric | Duration (s) | +| ------------------------------------------------------------------ | ------------ | ------------ | +| Starting trbfv actor test | `wall_clock` | 0.00 | +| Setup completed | `wall_clock` | 3.07 | +| Committee Setup Completed | `wall_clock` | 20.25 | +| Committee Finalization Complete | `wall_clock` | 0.01 | +| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 2.13 | +| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 29.42 | +| E3Request -> PublicKeyAggregated | `wall_clock` | 31.98 | +| Application CT Gen | `wall_clock` | 0.31 | +| Running FHE Application | `wall_clock` | 0.00 | +| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 8.47 | +| Ciphertext published -> PlaintextAggregated | `wall_clock` | 18.68 | +| Entire Test | `wall_clock` | 74.29 | + +### Multithread job timings (`tracked_job_wall`) + +| Name | Avg (s) | Runs | Total (s) | +| ----------------------------- | ------- | ---- | --------- | +| CalculateDecryptionKey | 0.13 | 3 | 0.39 | +| CalculateDecryptionShare | 0.62 | 3 | 1.87 | +| CalculateThresholdDecryption | 0.55 | 1 | 0.55 | +| GenEsiSss | 0.15 | 3 | 0.45 | +| GenPkShareAndSkSss | 0.24 | 3 | 0.72 | +| ZkDecryptedSharesAggregation | 8.44 | 1 | 8.44 | +| ZkDkgShareDecryption | 2.96 | 6 | 17.79 | +| ZkPkAggregation | 2.12 | 1 | 2.12 | +| ZkPkBfv | 0.43 | 3 | 1.30 | +| ZkPkGeneration | 4.95 | 3 | 14.86 | +| ZkShareComputation | 7.23 | 6 | 43.40 | +| ZkShareEncryption | 7.58 | 24 | 181.95 | +| ZkThresholdShareDecryption | 7.74 | 3 | 23.21 | +| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.30 | +| ZkVerifyShareProofs | 0.23 | 5 | 1.14 | + +Sum of tracked job wall time: **298.50 s** — **not** end-to-end latency (jobs run in parallel up to +`BENCHMARK_MULTITHREAD_JOBS`). + +_Baseline run: node DKG folds and folded Π_DKG / Π_dec export are disabled. Compare with +`BENCHMARK_PROOF_AGGREGATION=true` (default)._ + +### Aggregation jobs (`tracked_job_wall`) + +| Operation | Avg (s) | Runs | Total (s) | +| ---------------------------- | ------- | ---- | --------- | +| ZkDecryptedSharesAggregation | 8.44 | 1 | 8.44 | +| ZkPkAggregation | 2.12 | 1 | 2.12 | + +Sum of aggregation job tracked time: **10.57 s** (parallel CPU work; not P1/P2 wall clock). + +## Raw circuit benchmark JSON (Nargo) + +Source files for the **Circuit Benchmarks** table. Persist this directory with +`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without +re-running the integration test. + +| File | +| ----------------------------------------------------- | +| `dkg_e_sm_share_computation_default.json` | +| `dkg_pk_default.json` | +| `dkg_share_decryption_default.json` | +| `dkg_share_encryption_default.json` | +| `dkg_sk_share_computation_default.json` | +| `threshold_decrypted_shares_aggregation_default.json` | +| `threshold_pk_aggregation_default.json` | +| `threshold_pk_generation_default.json` | +| `threshold_share_decryption_default.json` | +| `threshold_user_data_encryption_ct0_default.json` | +| `threshold_user_data_encryption_ct1_default.json` | + +## Notes + +- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is + effectively 0. diff --git a/circuits/benchmarks/results_secure/crisp_verify_gas.json b/circuits/benchmarks/results_secure/crisp_verify_gas.json deleted file mode 100644 index 143ef26204..0000000000 --- a/circuits/benchmarks/results_secure/crisp_verify_gas.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "verify_gas": { - "dkg": 3042688, - "user": 2972893, - "dec": 3553795 - }, - "source": "folded_proof_export_plus_crisp_verify_test", - "artifact_sizes_bytes": { - "dkg": { - "proof": 10944, - "public_inputs": 480 - }, - "dec": { - "proof": 10944, - "public_inputs": 3552 - } - }, - "calldata_gas": { - "dkg": { - "proof": 169992, - "public_inputs": 6168, - "total": 176160 - }, - "dec": { - "proof": 169944, - "public_inputs": 17316, - "total": 187260 - } - }, - "integration_summary": { - "integration_test": "test_trbfv_actor", - "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 1, "cores_available": 14 }, - "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.614281708, "runs": 3, "total_seconds": 1.842845126 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 2.121472944, "runs": 3, "total_seconds": 6.364418832 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 1.95707425, "runs": 1, "total_seconds": 1.95707425 }, - { "name": "GenEsiSss", "avg_seconds": 0.758238652, "runs": 3, "total_seconds": 2.274715957 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 1.242666944, "runs": 3, "total_seconds": 3.728000834 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 18.979502958, "runs": 1, "total_seconds": 18.979502958 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 48.341644417, "runs": 1, "total_seconds": 48.341644417 }, - { "name": "ZkDkgAggregation", "avg_seconds": 20.006914333, "runs": 1, "total_seconds": 20.006914333 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 30.277848645, "runs": 6, "total_seconds": 181.667091874 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 78.310650236, "runs": 3, "total_seconds": 234.931950708 }, - { "name": "ZkPkAggregation", "avg_seconds": 49.050973916, "runs": 1, "total_seconds": 49.050973916 }, - { "name": "ZkPkBfv", "avg_seconds": 3.850818819, "runs": 3, "total_seconds": 11.552456458 }, - { "name": "ZkPkGeneration", "avg_seconds": 66.056590278, "runs": 3, "total_seconds": 198.169770834 }, - { "name": "ZkShareComputation", "avg_seconds": 52.534038875, "runs": 6, "total_seconds": 315.204233251 }, - { "name": "ZkShareEncryption", "avg_seconds": 114.608395854, "runs": 36, "total_seconds": 4125.90225075 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 251.230740403, "runs": 3, "total_seconds": 753.69222121 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.093863888, "runs": 3, "total_seconds": 0.281591666 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.264344016, "runs": 5, "total_seconds": 1.321720083 } - ], - "operation_timings_total_seconds": 5975.269377457, - "timings_seconds": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.273315 }, - { "label": "Committee Setup Completed", "seconds": 20.279577333 }, - { "label": "Committee Finalization Complete", "seconds": 0.007173125 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 5158.12970425 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 5165.210807791 }, - { "label": "Application CT Gen", "seconds": 7.707659625 }, - { "label": "Running FHE Application", "seconds": 0.0710595 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 835.140242834 }, - { "label": "Entire Test", "seconds": 6031.697821958 } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000821730b5f6c7306ca00000000000000000000000000000000000000000000000027d5a4288c59517c0000000000000000000000000000000000000000000000061a32d74244b4e4cd0000000000000000000000000000000000000000000000000002752ab748caed000000000000000000000000000000000000000000000005a4f0140d4a1c7f7300000000000000000000000000000000000000000000000512ac80c5fa266d0800000000000000000000000000000000000000000000000d400822869689e0ec0000000000000000000000000000000000000000000000000000a3ecc527c1e200000000000000000000000000000000000000000000000564b7881fe1989f5500000000000000000000000000000000000000000000000674ecde0680d8bf4900000000000000000000000000000000000000000000000584cd67f303aef6c30000000000000000000000000000000000000000000000000000623ff53a7e1000000000000000000000000000000000000000000000000b27d89433be674a5400000000000000000000000000000000000000000000000d03b476bc4cac62c900000000000000000000000000000000000000000000000d562dd63dce28dce600000000000000000000000000000000000000000000000000007014317d4d7c2bdca3a8f64dcf8eb8b35863e69e18fd4f152ed73eba57c25990e80b7c74b79013d6c8d4d7310cd110d6e43bb18fc56336d2e4da3740b50e8523d5f9594bcdd812036507369a09143ca35760ebd0173b2c22e17b4dc2980119e4fbf1ed9cf2a9021fe43ba1b07e24480cdba82eac2af02f2e755eb1b42e866b12ef874db8f56729f0b56939196bb391b63f552db3f3c7ec39a1d778f2686df8f01eacb03019c80d8bbf928d3be121e24970b738be38dd033d077d66095a42d4f1494b44eb7df0074e45e1376a363bcf636637b442460155ece719bb40d1fc476322ceb66ab5261818431b676c3ee34900e20139f2ce04bedc6ee159747b931b42f2b22b3d7198079fe5ff134d61e1d32094cc83972db87162171f68aff7e77845fde2854f3b9a0d21b74b20d8c2ec809d9db9e189051c0dd8557ec1661fa1a1b0ce322590aecc23e610a0c95bf8e2f1ec67fad3b50a0d2e04a267e7844e7e78b710b17602724a2b1fd0daf5aaae67ae6e02faeb646dbe547f0c699e2cb1379f50d28f36ba218a1b615a728d5b46c9da5f17803b7b0ddfaa6af8a8d6d945602cfcfdb1a8c2845e10ef52b08e4afe6f9717aa1865a5df615627ece311d2bfccb7831e0631a7271a08ce5712e16458da4091348c134bbbc0f6c546f14c8d9c231ab7975bccb9e02e0c991572aba8f415ac14e2e466eb01533edbb3a35795beea6327e635c6b103740f2b82ceef34e542ab504d3202e68b230920bc61d94d1cf18cde724d06e23a482b31011ca389f49559a044f8bc99e2eceffadc140cae4937d763106987d7fe30299aea525ef4ba58a7d10fddd62430590152bdd9c1fe3f5dd865bb2ce4e22a65086dd1e3b14c653011e7a3c89c4abaa1ed50be316fd87f42661de510e98dc9bf0dddcf46263bc755e1f5bad01ba6f03708b0bb47ea5aad02d6a9052b1fce2f05226a3e2e74566f10fe938081e85af521d530facec93e917a64abe8a781e246752f984676b56b5a9c17fb37ec862c78c596c128c75422712251c9e25f0fa4352a2a7bf93147adfa3937ec9733a4e035e4a297fbb1e9599d1de9a59b617dc808f117e08e894d7ebdc106e7f065fa3a096cecca51bf047ded0439a19e86d1f4a358099e717b59523313b12586636c72a0028ee88c7cbf053fbe465dc0d546b698cf132800fbde649562ed1d1c65b70ad4ba7497d90bbaa8dab5316435f6473614f501bf945798b6e787cba40d1202870cc74db20387287ff9fffb4cdf7a0160ea04251adcb89e4bed874a70342a96386f8ab23147019c7e3dc8a6f2d0f6ee77ea1b1683337f4dbdcb041c320a5df0f41519161ce81427ee42e7e55b381564cef1ff1036c245bd9b7f0292937e19a58c6f917d8bc00ffb2c098590ffa91e35aa88bf0f29c027c75a7685fecf83028b01891bf2582da15f30dbdadf169772056758b112d80f494136784c5e55d2ffd210cab43b12b3b22efff7fcd159f532727da6ec05f1b98a8978a14a941d2489d0948799285fecc08732988a56e66e352362a6be2cc8b3127ad5279b02a55f57f785b7e1db4de806714cb6cfc421b1af185f408d2beec04fcf9b045a9fe4911bab199445b8cabc822f63eda13db7ea709458a02c27135db9d0e4ba34442524e335b54395b3c5d1d19d856c4a813e0fc2303a45b50eb4e75e5e2bb32813c7346b9c9c1c0ef2b8cccfde4c1c8f4370c89efceb6be328a05d97d39aed2e4cc7d7536b34af28247ede4000fcc7b428147375abfdfb5516e986f5197eb977a865b855e9772fff94c52ffdf99286ee66fc4bd6ff9fa6540e0da9efe06b9e2cb8c70f7bed562275ad9485849152717537abaa41b09aef482b701578723527aa01c63295dddd454473177bfd0415c06d3d4e1848d50a58252bf4553e8afc869321e3d7796ba736c8b389b852bfec26444709e9d423192bd117d4a7cb64bb293e7c7033de3dc675267289c8ba6fcc0ca6ce0b9050e79e4e2d12956851fb28b58225eb8c0ebc9a45158c59a5655fdde45136ce3888dd460ddb15888a69073d825555fcd88eb1a3d7f67a4ebd571c54d2d1274518f65805c2382c12086776a4e11771b7d00b480fb1c5a78c300cd815f8039dc45ea68e61c51e130a50b94972ea631e451d8eb55ce9868d50b4e67952699e020fe4a972b44a36115a8998e760ca80bfb4b64576d97e16bf0d1c9b703a106482cf6872973e94520ce2037c03b5c1cf4892fe2c779650c50d7b88b19a1f6f4be7f5c78fb11783571256a19df3f4b96c9fce648218195e60d1c2e4c662d66ccb18a71bd0a9fb8a8b23c3faf51f7aef24669b70e1a4274abc0f66b911c67f15fd970be027f76d1aab14d557ba7d4f268f95afb5068341ba0900e85cc7d322674c057004d6fa17189123eae8a63d39bfc00d3630a696781bc7ecdc4a6b1d7677ca60c64c32d111d3c10f75c8f14135d4faacb046778787802723936036487091e65721e7feaf94a87d10cac1095e8bd1785f16c5e7f0f5fb9fbfb5faa9918ce2a047c7c0debec9fd5f2c41c01fe8b3062e6fb6e6961c09ab4db1da10ab612f4c5a7328a5209dba00950b62695a4af7c7304b3eacd7d23b4de36c705a55605659d07d7bf79a382923d82ab46fbaff077b68f5b592be6cac56d52f95b7f67123c6ef30cce46bb673b92b0ad6bccfde38ae2deb094a64332f2645f4cde17c0a64aa6cf9b79a7ed828fee42cca7f36d7f6b946dfd1e01db6832cb6f4eab7ea793718651913922d4f360d4b21f257a41c05813fdec7e7d01cd94552d86eae1bced28135690ba7c02f94c4102e5902efa5fcb489bba7b608b977362ed8ef889df360bb12d3f6fa737cc23ea410e2c92ffcb685b442e3884384753e48b3af21056bb1eb92dd975c59aa54ed01161b2d1f8b8e48050ebebc4226dad4c4efd3ccba5c481e80a25e903ed9bdd7992355c18cd5fab093b281f89fbcf4c49a728b3339e0a50b7c8f3eaf38c0a45a241cd2d8c6780c99d0506d6db1d8b009aac9b761cb438e732bebc6c473618f58a526589895742bb0bc9bf2157b0ac83056acd061226b5155c952999f617205e6231dd54dfe603fac9886d0279e7a02a79d39fc16d9590496af4854499544f0d61623b4c14179daa63917368e6315d7c8044998be25f1fff60ff99cebeaf702094322d12ecbd646a3e725894e99bfa905064f065a4bb9ecf069150adf4f6f234c7203c810c6f79c786b859baf6db1ce8395ded182bfce2fd1385b1fae68ce21305026844979fc3f6f15e3f01a641e070dac977d4c4e8251de5b25aeba717b43bcd322a34d4d3c3c7f307f3cbea1f6c4ff7736b1fcbca1b856a1d39a1de2a09e2ff306d5df092ac06ba92c3a7a18fa39a0a90e5b2c1d4d8645f0ffca3717f752afc42eb754439548876e6b43c3a5d94e1bf5abf0e7f9b5a6686050cbba01a352ecc21598da0ad02780190cf24cdcbc430d5aedfc4770abe9c6863cfeef593d95d28b1b050913d0e34830cae0602973282d94fec6205ec8756b4d696623c67d33e55e05d1cce77fdac4c7fdcfaee946a7baed22eb73fb1af5cea27644c13e2e249b6a0fce2f7340ef3aa4e2fa39063e6c8ed748a9d29f33a38f4b50e12d60c03433b81a80e41a565ac3c7013a1e851151847f3bb22ebefd9ff335ff703efe6189df8611dd23fff4444e25494a43eddb53fd61199cd4c1c04def81d6f77187f8f5e3da1a1129ba1faa492ccfa688a8fab7566278f412deabe1f69458de9a4991000314172a67e9eec385ebcc15e24e8cd28e117a2e885e418a5eff75fa6e0af53d4782282f17a9691bb7192b5b670ab7a577b331366962dd0a36346f28b8f56ec241ea0b6552eb0d11d855088e95a118fd271c8a3a1922ea0bfaf0afe9ea5c758e20492391a1721c25d590aeee4199f47b8e5c32ffea485cde82f7392674c9de1aeb3c183f9916e9789575f1b0b461ed9905831051ce8b19952d6c331efbf48b7dbf4614a5b15d0e87af9e76b285fd9ecce3107a983c726ae5d3d6ec6e2baeb34a9e390ee6e7510fc9579db80f9d8ac57e1cc886eaa4d2e4afe8430ff03b6c6f3aec5b2ebfab036e1b2efe026beca2aaeca416a017afbbf80ccbb2e72791f9cdc6f6b505b1dae159a08ee04351f5f2f240edbe45973e734d98f6f4cc9e2b7da8da6a110f7e97eed170300d1f8541ad1e1cb3bf6b3863a4cb490cb5c39e6d4faae2f6ea0082ec6630d36696ea41f90d0d64da3c8ff4facd1cec120e8736509315d426770883f14157bd101f004e3e63407103842843b512eeef44a77743bcefe483286303ab560fa84c14a40002cd93f22ece518b999cb3121c7e1df64eca2069230193028e8d3507fd7d7be0e586300d994405ce6dd9895c747b49f51f20d494c916c808ec85a1e118a4ba5f6448c66d7ebbbe036e81080e7fb9d38cd1ac591d5667dd060cdc04db6517473b7814ad9d757fca4904ebcbc818ca1f22ecf674dcc0c8ec1bb2763dba05f118fcf7500e370108e9004dc46a567294751b7ca51e51229b22220d0d21392e57177d6e2671cf3e954eceaca2cf87918287bfc1f6bf533708943011f277db9e4f9d9ce32ae3f3a1a3343d87d3f95b52d768e4848a92aff7dfed1aead60fddbfb11590d84445c4e75305bb2313c20df2f95eab7e64eb085ee372282e3acefad52cb23bf8855d760a5ded264e7cf806f650248c6db9c54b48e80709642a094184762433ca772b01c277a6fbf700c2046fac1e7c45852dc4c127ec0307dde473f72467a910331f49b44d665488a4c88895342608a78ef07cead2c305ad0fb767e03b49b8e665584ecbb8651dcafc1b5c116b33ba927fa8d15b98f5214a6f4c52b2e56646a8d9cefa2258846c3de653e2297a4051385c199d278a5926a6a8f3f0356900b0f226530e88bb4714c63d444b3dd57e11227770c75d1d2f225236f50e50cf07d29626531dcab3db32b7c1a3af3e43febe274500247334660014e70f3347b1f65c35c55630d4db54848774e2c140eb7c7dcba10652028cc818c7a328564594c77c3ca4d6cf9b0ce46a65cd4cf70a6820eabf65f863291f0f0c89ae9929974dae20ebeb9d518dd20b2795765cf0b0addfc027806a744965d41d9fe3385a89d328e2f8a5dd616160cda986b7d7ab9ec9543d99fd0f70ca38771d00e76228c39527af15a012f227bff39d231a2682a9bd9c4b45ca1fc2b260aa250577f8fa880edd4a07dd8115511301135921bbc68b53bd2375c7635fd9dee70dfc0e14e099d417eb4d6035da9ff7a9f647f84df2437aa344deb5737717058d0fff3c23d38e8d2548ee3ca6dfb23b0c4dfd22cc50c86134291cda45e0131e1917765092935098bb1a016aad380ecf5edc52dd0f95e566d538900955bcacc4342d16f512ab3e6aac573cb9a03082605ae8a4850e455b5d1641c03a37b0cf6ff90215793fd21ee0fc330133beaa6de3b5d59c40755ce8d0cd2c434b9a228fb79815ac089a2735a487f497d5758fa303de789f3ae7a09298147bea45a9c69ce19e2d058fefcd8ca169e2ba7760c96e7af51de2a28cde2d2174ad0a910dbb954f6e2fab7246831353872aec29ee4df899bca2eb5001464c97b1cce8c138fcc94ec40c8e76d15d58b5353e6b3f9ffc5b871ea1d2cc1fc1ab411671eb7d4f80c28b051dc71f34aaceee78f29a56e4529ca51c856472af3c41d587c25a3bb5ddd190062840ac3eb0fe54d3b1f28f63b89977405cd88ca9c73df613c7aaadc6965661c70747b5fc78ce643b6811e78b86b7f68736e2b356a934001d2ea264a89cfbe99a12dc049b6f9ec05d04083fd34b5647bd585f1be04af90aec97ea1e1ee9d42b841d7f31c6fd3853b6c12510c173a710adacac5b23fee0d5586900c83d9711955a1d5d41d21d27fee28b4989409d645d05feda97d3b12b1bcf31e156c75bed1d3d299392602de76f62b9103cce850a2b5a875ef961bd2f58321bb605928c80a122147f4095ff4fc0c4772cc9eb0052f677550724a61d0bf0d328fc6116e13e20ba1a7ab9f9f8506aaf4d953b7c13ada2220f7f6f4695fffeb8a712b5cac9f185ad15a710203071669acc41cfcb817ac9aa6d2ed7c763d9de8b8c7ac7374b0896db120a5f2e718e228bd07965ea30f302417d9f9ab3e01991d4090fd2b0920194ab038128867db1f90f4a79b4586f7602b80324cbacb580b34263c724430b9b91632d8d497956dc8231e14a4d8a202676f3b8a272bb1e26122f025c4f8f78d5c72c21b998aebd00ed34be2d76e4a43006fe81588d6b23eb5bf4c3367285c9a9ef2927a7877682d3af2d5e33c05808479f4521b3a17168e6c97bdbddb7770f93d2f01b08b7b99d331e78b71e84cb19252fb9f9f1ac65e844728db5722e55ab95b9f41edcecb4f31deb949896cd3db5cd0dc2ef15e4ba0eadb9bbe2657b321e467ae20c88064c8c15e19067e28939f8ee13304d7283a98fb49625609483ae02f2a30d160db7d5914120348062db5917327da32d027a758811aa7fd1c21b6a1dd8c75e09d22df3f7027626e95fcb651e8b7559471fcb5ffb9162d550aa82338c51cb0f02c653bb8a2b59c2959f293a4973748870484c05d4ff511fa666604692f73cbe282d4e0e243d6d9ba21e950ac205857f6d41ba6073034f315ef27929d5dba83205f192e83a1e5003de8d1c38c6dcfa2593bba4898090ff610b0890515f9bd44d06d0e8c2d93df6d36626472b3ceba0994cc76cafb660d6a692e7bf0559db1ed02fc0cb1865d7d25f89fb247bfa0e1a3fdae33f97abf5ae7d9e35033050445f73244d228055862735caebee5db8a119a9d8d11a7035555ada83e299f3387abf5f0000dea04c2732cab2470e3e04688d9f1dedd763e036634d28ba9f8e2c5240ca271898be20fb585b49b60139770919a34305e52be70f0d50192770fc02517f652e1e7ce1906dc26a8fbde6939f69b2c0645b8cb4a3954ad2b43918305a27046603a85deac0c770c7e523137f35e3000a0bf96b8c329525f65957437b757c262a2bc6a9c8527c1359d8e598afc781fc2e7ed7501a730554c58d2a552329f99d5c2635fd5fa08d5535ca03c6ea08662a9c52fbc5959b1a998374ed3f624bab2b5b2c33c8f5aeb856586abd239d13286104a2e90e79983c92ed579bfd5576668f7214523b4f53f4bee84986ad86798ec100d603f863f155fdfd2fd2cd72f5676a852c49103b1c0c67f55c4af21fafed6d1d9ed79f863c9807eb055b1777945f8d4f1e0db4f8234739e50bb75bc8fde5af7ab0688ff97d67209fce8a9b39e7b4d05d25548e0d72bccaffb3b604d3b33ce22039ed319792b1fa674d396a6c9786dcac294231be2234a234be2524f2f7dae91c55e58074b0503b24530a0c6e6297f19b0e695dacbd609ce9ca7cc5b2511ba837f4eca002f7102967d35389efb30000cb181f2a3daffd5d7f9915bf8dd1de5d4e7659638e9584d13714469a9ba5d37d7313784f05375e9c5e34ef8219292be23da93ad73869d0bc71516e4393f5a782062975871276d39f2f41d36b81ebbc221648befd2dd8b9ae5610fabeaf54f1947f17dbe4490a05d99f3ac74e0d537673afb9d993294d2aed3e7e293ea0d280ceb019e6f0704f9b34dd1a8da3dc616c365e09b09075d2fef5b1ede4a3bba6df095b1390ce5a31053ffdb83cb05b9209d37592156cb464012744e90748e708378d9c04416b2fd032528cc00e516f1fa6bb6bead7b0d0a69d97e0a14e3b40ada5065b101efc0f1f4cd612b723e18127513fa2b82d5f5cdeea70c60581191b3c7cd81f1cf121cc958060e951b4e6c76f2d557bdddd63f9f51676352d04e0174aa85a900b4a92fc1ddfd06e8006f1d78401e5f7390ace2e425bed97aec36ff7e00d2f6525871edfb56c38063860c618feb09c8748f76f88ea04b6ed77e5421025254d1b2ed05884ee40dd3e9536f678676913dc230188ced021bcb986b6b18b475fe0ff25ca62e1660243f9d9b164881c2398ee00b789bc8c4444d3682420cb869165b30e224323381cb5cc73620a7a96119458441256a6229f2bd621cb86ecc29165db17aeae0846231e6b67942cec4406b326de3bf6da88d906298be18f50763420171db37eb6250875d716390e1399175ee4eec8b0bd26efb179532d87783746b5521a5304ec07e9daa765361558c3187a2073c503c3981ea7992d9c60bcf15ed2832f492d65ba407662a090ab3ef91cfcdf450a588b143f4cbf6fa16010ea41310c305c41056c1e5d407b1eeff592b7fd4f7321aa7e2be12587c37faba2dae3b5050e6c1530a67f22db6c8febe5f34295ef550fc8bbc338a40623dc6065c708338604f699048cd976fdb44878c7b1059cd85f4b91b964b8df3bf9c70f3c2befd9322551cd9f2b50fe2d058916efe06f883fde51721cc8523939a3350133f533f1810524dd023854b3ae2592e3c34de948a860ca68eab2e7515f901ba4ef130450802767ddf18f04412c57ea96eb43689d7148daf50f18621a2a2f4aa76ff98011bb12d62e69c1c4d4a8baae33a8117b84857e317f8562da97c5769136574dccc90706388d13056cff5c7444a3beacfb4110d60cb4a41e70cfcc8de69c72a693c3c22cc4374fcd2cdef089cdfe6a0c2a0e8f8acf27d8e36b650e13a8d51308a25a6829c36bb3cf8ad207df79717b3a3fc45a0dd3b5db984b8293dbaf91f785fe0fcc0c2b38ec1232d147cc42ac3808a34322fbb5f8306303cffb36614394275526e41f473251fd02c680ab57b99de93a2ef421ab91e4426b4c99f3c6d03504de269b275d2cf9faa91d300dc7bbdb3dda6da71347133816be7b051d48a33903efbf4407f90419f1332cc03e9fd8c41e24a26c8f42481b2e9fed3327ce1798263434e51fb358e785403ba67331bfb0f3133ead7225bd117acbf86d18a8703e1d3ebaef2281f87c90eee0cdd19c503c9e39020cf0a6f0793d93dfc23d0167592855aa6516320c766eb5089fe5089ff9c2a5f43622cca3fa745cf3e61a9ace7b7e1c1efe1bdee1c9bf8561b342a2cacb2584c6ae87cbc23ca7f21561483dc48e65ed74d70e36d273946c8af8fbfac07b8067c7537725fb15dd74d6fd8e06d9560d04fe9e0bb9cc66a552723894d500bcc67d9dbbbb2f7def521d214cde49257da5ebba58127ac3ddcf681962da25f22c4452c92bb8643d2d51cd72af07b36ec40933aded2a7ca13c7f42fad892ec2a215c0b050a497585d5acbef59321362e9ebc54f7d7299414c10a9a83fc9beb917a3272cbfffa4e7da3fe84a0f10dabec1f7e5a897e0962a0e842037b4604c054cd61aac3a07fc1c81f3a72e51e2ec990d4db00b9eb14f32933b1cbd476385d2d315eb05da63cf9aa7392e86f8789f1eecdc9aae8fc14541b5d0cf84ceb4c04ef83b692a470b986b75ae8fbd52ed926f7c50a3f0a5c0025385e05933854303b5fdd3eb968d912173130bf0b081ba8ac3f93cdefc9a8143fa61d6377db816b1367c023307d2fab251d5ca6e846a272a4577a7fc82d0901acf57ea102113af63c159c1f5d0d91078ffe6f22541bdec846436cb3729c8126556cf495ef535047d5bdf6cdb5aff1c08ace3e8ba81eecaaee4bf99b80bf98026d93bd1b87cf7eb21a4085647627e2b0b2504884d459721c7b979153b14d161ecf00bf75bf12b4c944c0096897bc8c53526c746135500931ec160fb77114a317c0ab237646eff1345dd26ad855f8d7f7228ad671ffa4793f582b208bcd2dbb018fafa828663cf93e8ebac379ed326fcd502f1b27bffe1912716dec34b4189d2949271b39f8384eaaa45e64fcc800b5f805dcfe8e9986c26d7541bf962d1166044483913e751c75bae08db36edb57973712fba0c80d6813f1e051c2af60ef6e117a84999eb5ede01635927eed50f201635f19e297f45c406e75470fbfe8a40f0db953affc8f61b592b5b0bbf515eede750bc8a0593edcaf5959a5445a1ae0c1130526a514a02445e3a6978f1849f50a7c23db9340d42577892f4d385814e49b151466ff20534e74439acf791d130b0f765f9c9b2ab66fda9098c7771834d71e0ddad7d678af5183635414a7ee104cf52afc1dd2ce8fdff5ba21534f4d73667501ddc7df289a79ef1a336f930cdbe947883fab991b26adf61eb7837d582b8e9d15ec335ab0809126826fb09abb2da87e9aedca01dcf1a662b74cc555fab8b65c013fbe0dff90977f2a74360eb7fb0e75c59408389757e17ad071d76c112ffbc4014b0ca129f864a2489339ed260cf0796380943a2618f3b13e66649fb04104ff15197fafa3b047eff947a782bea063ee95bc5b8cddbc7db60661db0e6a47be4619fe21e5918752a3f48836a6630ac27392a10fe2b6a6b292903709ee2f82c75e0d667d9b51d5ce9ff23fd6c7f2c5a87f087748824f9bb170c33a51a704be5f042a94345fd5fa26e80dcc485aa26fa6c6061d6cafe422caf1eac52f8405a6c0aa2bff08666e2efbca4f1baed7f5532cb579708baeb3555ae6b984fab00610901e1ba452a848424b71e55829122932331f5306b62a5beb24c70237eb9c0e0207591d783524c1a28c1fe61f050b6bc6c300011f151959e387c0125b787cdbd2c7241f837fb79f84889b30cc8e5e84cb49a895585e78ca301052c5df2a4fc873db770367667202002946d7dfbf953f113a006ad4837ea696baa9272c58c9af1d6d8e1f0f21da94147cbaf178fb414d0a77e22c33c3fda3d0be7ebcfb9d95daed750008c9a1d8ddc601df5867b8277024e8c2f447a739164cc63ea44c03ad55282f181cf08f3933494d3938e6937267d1f54289a4e1f19d16abd449ee8372fce0834b212bfd7c33276b09f5ad9221383f3bea85ff25427d5deb17e7a510d4104e32aa160ae05861837210115dca42c986c81002ca19b8d3645d1b473134a160d34ee3261287fd51ed3dd9518c3ca8a42d99bc095a056a3a51411fe822eed25ac7b9392e319e63cfbdfa8c25b357407e9f6e2e249f9a75b5fc17b8940b10c7a8bdc0c80ce7c78c5a7da26bef638318da682b033778c232ce708d33e025782dd268861c06d6ea5cd80d15066128b7b0b5d5b6f51f240ea176d0580ef7b02eee47a5cce712042e07964c9c052cedc831ad39cb143a888041c8aeca71ddda00e103d609160b7ed571aa0f7dc432de1024a9572b2f46029fe8563ff2fe6046cfbc4589099a139ce636ae588318153e65aeb57232762e7be53e82ec3c6f46ee4f923625378f183f5c778c1d9420a1523b518c7b970ba291de12f4ee0d3abc0daa36f7faf05901731bb21c7b0c1e3fa46a76e005632f8eba62e811233000cd2539f44b06619c0391e67d0b33b06bdfc98775458a4293c9515b87e5c25f6cb6d29918837096890ed9428f0cbbdcb8eb0edc385318bb3e61aebb5774cb91852ccceceae70c38cc2056a75e1d63ae67c774a818ad111bf39acba607cbe4864fe74d45aaee92912e237f27cb4f8d40f7029eca2bae4caef946b9549348e37161a6e56168328e03490e955c18c7bf6cb2ed0f8a4c94bc635ba90724e62b29de6d7fcb2249d3951b32012abdacd304147bf0b87a684c56a84e4654a1fdb1c2109d4210f7e9031f3ff9280148dad4fe9dac49cc6815a9d2910e9676cb332990e6564537f5a787e126bb154cfff073cfacd39e74a57c4d0d382155eea6093b96ee0e0bc1d204f58742131e755596903ab9eeb3e947677bf81b62c73f504acb1adc7e4dee30c034ab75790ebae3861e8c094437ac0d5cfefa3b3b0dd75c18b443e46060bb69f122660a7f0d8147ccd093f2aa6b1ad705f3791229891faa6f27122529f42e149eae990fde118d8dd0656f0fa9f5267beb6ccc40fdbbcbf34de723c173979387831ba95cc811e1c79446cb200b31e31f3200a287f582be616f920f3b24590529f1abea7c180a6ce5ab8515f5498960440ddf5000079fa233245d1df8fd688ade85739eda4906703d70e7215b65746facfcfe1aa1ab8c7c817b137661d535ba08aa63aaf44f2b87690dc15322b2c5cb26d729a557bf30748c7dc60c36a9610c9e758330ee4e1efec3393675cfce7c64da0c1fda8ecd235b8c76e3681cff99daf8473c686d1f0bf6aabadc7acc20b88f11869b498b3d839f679d443073ce6ef84512ebed8821286504bf8db57ddaa7f07b0ef748604de3498c5a8be6c3b5b8121515bb8306782ca30bf541db60bd1eaa09ed005970e3c80a2c7c143f6504d10b208ca352b8880e915045e50f5ae5d45f7401b0f70076e20ca6635e89bd950b832ff0aaf0236e206fc268a7f893c058c73030c70f4c7deb51ed388a3bee3aa16534d5319836be14f0addc9788e212172376c5f8cfae54ee2cd8792ae628061ce47cc1a22869761305fc7a5369bbe86b6aacfeb72427f397e40e8b901f5b3e292d7d29160a11bb05979b806f46ca206ada7f1ac05c54c4f49cfadcc39cba3e49e3ccc263ff77c319ceb2c976e8f0ed37f6a73c371cfd76d1b82af0a1e58a6f8e6c0068076db9c229f59e929d9547d2de2db72aa9b89fd37dc41561ab1809614d396d06eb5689f00b058cb17c2b3cf46fc636066b4632a5023dc55731dcc76db3dc1a0a0ec84c900a469a7d17720805a3c2bc7ff493a2df926d49a394a7a0c45c7620f051b2ef14160d19492657bb67d1bef596d29b79d29a4babaa5428795879ef66d3aa7dfd7807592edaf9d568afc173e452e112179d987725eeb41f1182a9f2bf32db42b63b1898abae8d596b33efe7b6607af0079dac5d356bd7cf13d10d0a4e0768f2114c25ce2889ce6c34db05ff06fbfa4b955b018546e27cd0bbfb2bc0cffa0792cb5823bab8a977dfd68e70f068ad03ee38aae0e7d49a8957c3878d643e6d0f65fcec272c725699c16e846c8638f5cec0fa2d981165b3bcaa7602938f4a2149ed71e10cc23a9c5e7d0a3f5461709b23cca9f4eef68bfd06a6fd5e505e756ab4c40a8e2859e0f20337582b5f4556a47c503934f1b8ce4ba7b27a31560d3809029ea2d2067381928499d6943b040cef2cfc40f7025ab51d66d46db1eaf2dba55ffb9d472f04933ae5c2edea96a0468675bdd6c86d3446009068ea089303a1b9d053a8841d98c5b5a491cc1f8e8e260a3655fd3a6b3dbc20beb8e874272a67260b49afb0213e712579743fb62f8ac169ab6500175cc164c2a5caccaa675159759f708016141c4e101152c0e8e55842f9bad78fa3d8c4ed129f91d6450d6c06e85338347f0f6a91a32ba22a390cff1ceb374f5e2b6745e8905f0866029a4c2f9ea7299cf31ea5b0b47ee4d6329b1ba0f1ed46fb0be938a45a2db24560edd0dafb2f1d5526073075b6b2bb62555d44907340a599754fb868566a768b458d6f6b038866a2512c37b7d743fe40490af6e2267fa03f03e2a8f9b6d95650221eaf0b4c87ccf6d505125e1dcd88c3be809632f750178fb8b6bae40b12c686a256feeb481b20ec7a2024b45fd5c00f5608c9cca96508b2425fa584455c1c79dea9c947c13e54f2bd0b77f70ae6e1aadb54cd3af5dcbd773c96a36c60a6e94d41f051284397b36e4102431a5c1227d7a2e5eb432dcc11f0fd5884c17eecfe66a1ce2a22b0419b751d117401dabda490ef21888c13c136e09941774ff0b070e9b24e762ea7ff7684822e8d26105cc2ca0b833708f06574954f6b582e8053824a60dbf179e50db6f55c069244550b03c11e5cd22653891fe5064198a330115698a1d852f57c32eb855423c9db7c5aa271f155577ae09f6e5a7e32c2aff1f5150feba43b795cc2584c07001cb655353eec53d999060ef4380640747f65e57196198444330db72f8081f5202a4f8e4f598610629f96fdfd5734ff55a7f4031c8538f9fe80d1dfb973e8d61beb98f1f854d9783b319368ae6f9a015484bbad829d30cca00981f840ec563a2e24c11cfd9977d1f787fb0a395ba4b1acfdbf6f2bafee57074e5328affeb76204ba93ee5b82ed1dded622a843db90a4d19c0b4ef46867161b3f6525493b3a3e285408073fd97e2f53fdb86c06e9845e8d410462857a2b9243b348ad17b662cf06686483db4b559d400f33726b232420a522d3a4a63376d60b56f5ee7cac6688203272656b35c2a109ed301e8a298fb397d7b66d22b4867b9806269b28a5c16d2c366e20f6c78ecef995bf41e82d6fc7cc578e293bd917b60d9bd6d1dedd21bd12b872b9d3e443055b534b753d81d39377c29f810995cf6238b69779a358fb6208eadcd48848b22b262ad65f2682172834b865f293d2fc5cc95538f242b21cd12261023dcc253992cbc1c548d996c675b14dac9b530fcc0bf78fb90bad6027e52502201fd2f781fa9dcf4da5c91b6706f2eda965b330521737df12d13d350c2506a779924f2bfd8c634cd73c84811da8534e0b14ee42996d6ed01f879d4d858605adbb356eec9115a7e0a689e0de4c27be333631fcfa9fec8d02744e206dde6110f97142186a44a6f4f220b1295b4e1b0385b0826984a145ac28bcf89a9c3627084ef803b0cae77c7eb29fe2d6ff15e3762928f2f1100f4a8b59adeb81a445e70c54d7b8453eb39ec2e05e612d1c0884eab9b6d8792606e5ae8627d0f4d44beb2d4306f31b6cdc617da0f5374ae79900cd9b9f693debb14d59889d5b802fb8471c0a414faa8abb86d379cf24a883de065656be4205dd39718b1dab3ef93ba2e5107d2c79ae5b564bdaf2971a6ea04febbcf5e2c7b3e3743d8b37631ed2ebfc2a116cc9f8b27037cb4d7bd4078692ed74caa2a1ae3458d1eaef27cf48870bd95a", - "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c91109676183000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008f26088c208eab68279f6f45f234d271000000000000000000000000000000008ec0f15e98d9058c0e708be73acd2b9d2197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f2a318f2fc85748ea90c49625631654288c77791d8dd0792e201c748ca9863ac417b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e2bad40c7a2fc975f2e4811607e4d5fb72361dd1af1bb2364be799270aab5c6f623db21355d312ad82ff6334e39518d7aec226005c56d124daf506a44749cf370" - }, - "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000006443e4dd7cafbc6fc000000000000000000000000000000000000000000000009a5b11556957a2fac00000000000000000000000000000000000000000000000481d8c87ec8d9e44400000000000000000000000000000000000000000000000000027e3c69ce5a8800000000000000000000000000000000000000000000000c89919183396d2ee500000000000000000000000000000000000000000000000156b5f6351b4e1677000000000000000000000000000000000000000000000003fa278d2c3f3cc83b000000000000000000000000000000000000000000000000000257547a6577fe00000000000000000000000000000000000000000000000a7acecc082a3669e1000000000000000000000000000000000000000000000004e4ee60a9610ccb0100000000000000000000000000000000000000000000000139a7b0dc64569eb80000000000000000000000000000000000000000000000000002797aa349525b000000000000000000000000000000000000000000000000e4efb10408e5e22300000000000000000000000000000000000000000000000565dff2497b05203c00000000000000000000000000000000000000000000000c3ee606bf31b8fa98000000000000000000000000000000000000000000000000000144da9a140bb701dfc1ca1fdf6f4d4992fcf7bebfab3317fc2caf844b02fb8ac4436d5adea45f18531115019e88ab0ae6f13233c1014519feb8d5364b2b488e9d836182eba9d800f3b9f16b517a82e074902c3c99bf34e7be3eafddb913ebb2c8d05595a159bb2a543541d15947ed4f1c1620e84108ea3ec32503ad7d073162c30b94474e387c0cdcb845177847e28da58f2486d85540c5484ae86f2294cb46b4cb84d0aa3609083d483a8b920f898636ed7798e980729ee4ba163dbecdce8b02670604ec9b920e6c4aa8dca3ce558f7cec8fb7f4bfa1b0d4371b18bc835220613a1409fa080f120c539f947fdfeabe4f2a16449a964098c614d6fc20e3e80d0403f0241f5d8a13147d749da88cd2a0d8266d4380e5070e0fcf87e2e312a3cdb01409afb8ce082549d88678d5f866e68a4d0281b41a6b539a157e18a18aa47a9d801ba82c15b4285ed10e2b52a839fd1fb5a55f8de22855f4af96518bcaccc2dbce494a296ebe26360d30cd1d486f49978b029766d49825f3c54d91bd255e22e279a69caf0742070495855195395e1742793b60d1378c09dc9c7053216512cbd2452209ec4be4071c5a521b696e975b1060278c4f77f59bb6ef505a7223d5245c2faccf2d7a7b17bc6a08396d8036223bd5f0ae7ed2f2891a3c4e8846b79bd36ade7b09fd6627114aa182ae1731eb6b9cf19b8b841c3c2b6d8056888915d6f3833646d375991e22759e6ca02fd6241ed37565eeabe5d1c1ed7734680293580c5485cae983d60911a42e17e7646d7d5bf1b3633d1bf857ea6d0d6dd6ac3947070d93be4e391a921af9366211aa3384aeeca7a871208ff44293b9bc62fc7a31c346b3bc0a41d8ec2faba055c65299d4680c399879ad3b09b91ca75c154614e7c2365c05d9e1e1bf0312970f67ebf20490f3b18aa7c84bfbf44044a62091158a689ffb7eff443a0d006f17dce991bb9008594904f9599b39252e57e6a7ab50656c9e5a8df72b58d51b7c9f4435e6f52a478dad1fa0f66a4117c91477773fef118f1a5b2e2246a83126b004cc496b97eaeb0939d7fe1fe9df4c45268c13f7acd87f8d7a7251397537066d564a275044ff3e9013c7416550403fd3b89f31931a1c9bc4c78890d6039319c12ba5bf473841715e3058c08b58f6c885980ccb7196c78fbab8bfc448f50b23e59de65950bb9e7523d7c99364b96ea7e2b23207d02a56f9a6ba47ffe607b0110784790f007d11c1a16dfc27e06e1ca443bf4740ab87fe60bd2e4719005c8706dac42041fbc232faedc6e10c6a6f75b3fbc206b8f7c1648c1104fa3deecec91ef7ed44600c3779a8af114f9e25af821788f45bd741c89b36e53c6e5e6e983e23f372c640298eb2ca3865a651b2add2714020d6de218a78a66e53d5eb9c037921c9f13da3795f03f8a780a09119c28b795e6e6b97d67250bc77c40a81ca7253285c8d29c99aecf5b4aec5316f45c7934a7c13e79ba5f526153174eac6f0fa7f1d5423429dd2204d5a0749d4f1d72ded1463077de54e2914170e854d559814782791a449f5831160a02643aab224d9ccadbd7c12366cbe5895233711003e08701b69ae17495eb9c96cb4741fa95d3a2342926bdd5a796de050b055178f9e91ba2582ad1dda122e9168349f910fba42f2389be4035b6a4a0776ec380eab4a7e1818da3933777a20728cd32e8aeb3f6dc1c954b5e7ee588996ffb14ff769561b1427200b52b53eb0c2aeeed7d7ddea931eb9dcd23c5652b768cf3fbefce53500b8030754c5c1ca1108f3ed46afb942bfc01b032b26a8ba44e62c0365ca4033b1b219a80f8a2ca3ff9ad952bbfb2493969d7eaa90cc4aadbcbf36dc76a0c848bbed2978369892c56259b660b0a07ee28fba8b592f129d029bb31b80535d8003742403319cbb781f5e4270e2aac914ced8a3fbbaaa3d9f1e834bad9b9f07b44814681c3e7cf0859f312e121eee65169a441b1dadc576870842c917a36a744b4f4da91c33208fd45e9c462ef67ea2f432b555a46ded01dc8ac89fafc61142efa714650817710edcc68e99b475b7ab403a6de405f94656a27d39fe369e561b26a5457c2921ad7d3ef554ad5330d553e322f95d5c40c50efce4a009493e71875eb2182d0f82765a780330127718248a51508491c0d2bd80766fc4007dc818073c337f882d78d25f805d23f12d1db526fca5ffb5be0e54a6ec25e46bbda65b6bf5aec648184761fa18ff033b31c97d42fd3a43ff3f7ecaea4d304d336e0e917a0d2e4425050be6d7fb830cf4562eb87b213a5d6a0dfe7b3d63f8e11e3b1efa9411183811071ad9e3d533211eb93739574879ef787af1314cbf7fb4e874c3ce87ebf8c3f60f93f0b8ef56a6f27811b193c26971d3e773e76e68c5daf56140ba20f67be6242773bd78452b12738a0ec3b6204e93c9377acc0358971d74ac9bfeaf9245d9421a7d20b5a4a2ecd40e4d23d71ca5a7e131b932acc89bfbd6866b38719ce78d212cf028cef1628129d642240f4b2730357494ac27446f5e56c0978e0c2fc239ee112666f0bee00ed47f0164d22a5215d5db82f3ab6cf249bb50703d3dc27d71423028a4f72123edc079901ef7ccb56bef61f6591b0477b23cb07795773f20e2bb12e5e0ef459fea65851cfcc4b387fc42cd5e71bc089a7f4381c913855a4f2f1f0106cae843799f89fa4b279a364f633a37e4b2640c69857e61279dd803ec5c6d039606c9c10584fac7c1d5b10de7b25738d89fc5569329afd79e885fdb953cad2cc05964e713e4c71e4c07ff1cd10c406ff55cbd851532a4839738d7227ed257241f748badd25148e8ca89366948ebeaee84d613987f693e0d213aaa594db3021fe702e566a8ad2d75ae4f812859398669a51eabc475fa34a0d77bb9611932a904ba84084825294979e0ede81171919e8bbab7354cd01123a8d61d9548b311ce05d765d5f5322a49e084b9796079b3196f5a5ef6761db7007dac93649c4a387d02a34ad68acf136903543014ec4b3d4a3fe900211ea22ea18eae5100b319d329034f88cb7f95f41dded0a0ad75b68c71278a61b51c19c035158fbb8259d36bd505735e825c6b8f845e8845155cb93ba122d3e98c3db8da44666609cf7192ff0d15d728de03a78427d9b166b5ad8555e4b65afb3244bd53993eb496c5cd50833d22341df3b6674d72bb8271c4b82e7f03740f15b18a7ce3fc8943f81f826fc8fd227bf5ff41b0989a237271e140ec6ba99a0c2c913033c1dfa00f9613114733a42111810a91eebbb5bea13246518438196d97dabf006219b2203cbaf73568d7f8077e01b07756ea1d188ab0a2d7d2b901634618048e281da916256acd484950b7175c097adab59b620eb4718fc51493f0566c9e30124ed27baae31fcb691fce092acd8a3e8ea251794afe8d35d55ad7037ee89476c111e04610f219a1aee124c22ac4339cb28af586a14d986b328dcbc25636df3bd150a77cea7298745cb7fd6b2ef63e7413dcaf3505f98d6569f1e4c8f3d5082903c2a15cacd73e75fd8df54e2307f9e71217ebbcf22bc61abcb8690a22409214cd7847da52e761f7ced049a31c763c92153a0b2904739905c9cb26452f4f573ea8f9a7740b72e854a50ec596115078a8b11c57010a27a043652ff97f51ca448f30614810e3765b8a4149a63e291d1683a4b9a0b964f6ffece7dfb0cdf1c4ce4f0fc04be99672261f50284503148cb9351ff4662a49ec4803560cfbc9165ad379fc90ce53beb5ab320807a8d91d0a10934a2b9e4363934b89c786d314b1a242e9128fccfa473dd4fa8f8496710aa112f31617661961f5e97fd2c3fea061bbfb54946e7687ec43dec8861421bc140c7c1bdb0a3d9b5909cab572d3434a2431c3236a57baefb073fd1bc6d6d1912532702a84570676a2eb0d0047ac4b2bc9fbe5c927e3dceb3c5c249d3cadcbe929008bdca758870e554bc839114d0ba80cca1fca48d28166f949e52c54e6928b1b9605ffb5f5f6b3db23931f7e59b994d47f7c474a3564aeb179664ee01f91770839111d61a992bd1ce5fe02631dddf3d4ad212e79a3aa20c635f97ea8d6db1a1bcf6ec685335f5a88413279438d62e691fc5848efb92ad7dbf44ea4e6f23a8d20414139bc656a727e0ebe69c8a9dc3802d314028e5a8643622b705ae5ae82e01e2578c9d36a252ed8138a7aacac14849121aaba10a66d6ddde63431e9bae2151950ccdf9487c6874ba926706e38df897c4ca3183893c211c3846182cd2588da2d398a58df3e979a559c498fe06a4a7a0163ee286354b8da07da8b9eef9945c618800d7ddab549c895cc7e2ff3000f1781554884bfeecb44e4124d603ebc19b829730a60168980a8fa287fc8f86c0a8a90ab8f38e71f804bba01d8365d1255930ccb79be29beba86407961ab765e979b3dafc32bb1f393d16702fbc3566aedda089832173ca5a7e5069fad1e0660959d49e0bfefd74d2314b393f2fcccaf682a22bea10fe8179d3ba752e8044853c47f4683b4310914f61e58b7a1df42504a74113e6facddec9a9ae5b220a1d6cf4fc0415e9f725e11104af98a7018d5dbf4b5132e8a07a7b4803847e32293d13af6a33d297cf15e40db4bade0a4774ac86b0b12fbacf70560a28f3f7b4db8fa73364a46bb2eb57964005bd8e58c2e61eefb861e701eadc30caea24196fdfaff46b16dc10ecaea90bfdcda36bd80ec0f006d9d2e1bcc89a79ee552a9d1d32fa60bc21ea126a76871ff91f479eebbf887884f360274da76723b26de1d7a5e8ce9ee53c7bfe8b5f29ec186a407e75ca0e3d661862b867f5fe4b79871eed93603d868ecbefd6a9b1188007d91ab5aa9944fde3f6b18da6194ed89cde701078d634b6c54192dd528fa637977e5c3675f4ada2b502e13be091100049adcf52752f514bf95c3fe99e4e20abd509446b5fc66a8857e1d2001066787b655dfe8931150d5648b382f60bbe72d6e66c73e3e6529d54dcf4811cdcc0314c615042bc66bf5173485c9881e9544493687da1731b6293a9679fb04b9622e9b578c81c7608e01f3053cef3f632d4ef5a58f172e8bf12259f6d5f21e25a74e2fa134a379f6308d57c51335552484fb0e88867c9d243bc181a090cb031f58e0fa9f655929adfa77a05baa4894c7de6a6d37290d3eb16283ff637dbd2fdb3500df9f2418aa9a9f250fde66f091fb6ef5acdb61a27902e22fa6d5fdf0057d28171834d980f73ca8ccbc0b303a5062a068e03e7ecfea78e72f78bf920d00be36994a8ee4f42a36ed3a223fba9826a967f946e75760a85a595015b8af2e24ad7c59e99b87f60a5fc7c76e351614576c43732e32d314ddbb85daa881eeed1b150a822d9945663292ce60728bbac00dc5ba3c649e814005a52df5c0fc59b81011cfe6bed95a7ff1808bf7c6e1984dcfba5975ca1768a00381f2974a32d1151e75c2894ff4581c65de16e354ddf938c056c93a50c61579815a9f0282488c76145951a635f929a71f8e662224c9364e12748d715cbc7bc93c5606f07687844222416ad582df9b9091bb790236a28d090291602b43b80c146036ae1d2d3c911123f37a291933fab7e30deea52324ab9ab3adb486903c9960992b5ad8cf6d89670e608c9b4061890a793116ccb7fbce38857e020824d1db42f14fc651164d22a60aaa8f2ad0f20b63778277b1f641d7fc5d12dae47ddfcb2d55e6b870d8b2814c20ffc73af5fadee89dc3b3cd116e881d5c768520e9fca681290ab5c58ead371022b65ed2116f353e440fdbe5d2fdf3f025e2a6d2e9d3e94c9293ceca8086b0de285a7305750eb30cacef7b5469a0797e42080970c0c3e70f990892b6ebff1044023dd8d11b77a419b3aa19d4bdbfabaac8d553dc042fe6c4555b6a14fd64ec980146ddd60ce32d76980b01b71eedf0bdee8b18f198cc0255d73108f17b509bc13025bafa936bc61579f8c1a9930017d1f0b042c5bb0566743b6c731e05e3bd7d1cef12b035210c3ecf81d2caaa99da32fea59317e383b970e419f861ae2573fd2b3c6b4f0c1ca9dcd2203e4b1be938e7838f970bbbfe78707468e381083605301c190b6c5f06802b1dc8b2280acaab86ac5046231ef1060033cc35ee76fe2baf2d661ab0c19ad8987ab3cbb89c97f3c6368ff099e09d1febe8e7f6ecc1479f972b5c27cc320c875b5f129f3b6ff24b4c8a2eb82af590659ba225f3e190fa4e6b1008897acc6e797f4357055c26eeec55360f23d1da9543b6ca7bb34519652626273e877efaf30df25e18578770476876265c499564d49f34e9f6a51e30b1dabe0c32c7acc142ddde363e9e60696c0fab4def3165b7cf5799816352fd5147cbbd053810b943836eae47e715380acaafecdaa5c56f78ad25cd6beb48b30b71e97224546462e575474e305a384c11241bfb4922692f7ada05ae8ffe3eb5a61f4a432346ea3071cda58fee452fbc4db70cd573ef7d17106659050ca1418bbd2681da0af56fd4cc9e2c6418372609222d7387c272a336e2b34a001899e647750744331b117ae4529b0938e4e4712dc5686b6caa250480220d3e94a35b9632e3a73fcd126572f8c3d4ca698b890cd2cec074e5e7a8a7f6f624f385cd36b9d21144013e1d4d49e7dadd46d3bb09ef8d4358a93c905259d943a18b6a020e9701b77274ef12ca1ec8b6d0d112d3b1dd49f29c7bc13ee5e7ce00c1e5ecce92a8c5b0b5b464094dae99c3561ef02693568d05c04104963444dbfbe0e6cd804ab19753689d2f1254bc80d6a1dde41cae49945131b625cbd58bdcf37f4cf699f10aaa7267e1d62acaedd0bb43e6003a32c7112ec16756df55e6b945e730898cfcbaddc25621cb0f3ea6045ecd0c18e3c46ceeee924bcd8b5931869ec38afe8b79b6dcec6e649f26fe612ab89de39ad9fb9d355f67b6608ea70697d9f7125cfa4831a71b894a0a0bf5eaba34f2e38a421e11ed8e30af9a02bac691e911dfd2c5cc8bab6fdcb23904e79827c342bca3644d0ff0be3bb71ebeddb24e04066ab1b9fbb82705a3266a1b742992abbea7a4b968075d5348ff4e1068b01de9997679713f13c50b558d69224e93ff28adec2874a0f0131ed5937a9a84f1a1d27815ef6d4412936fb2d9ab2c9f461e5eb9aaf0b937abf2c25c62926c3052eebbc13a311af28dc949296b271595a279367d738f840aab4617ae5d46cf23b6a6f6b932ffc79d71daf27bccb2128f76718008bc6be9dcdb3c0b25b5172b1da3752ebfd6f9ea2c8c609d906b690b01c120df01729bd22d73b94dd8afa3b34ecbe74d60ea6d449c60645f1528622ff8a547dac1025fa5fce863dddc98f0b30fad91b413d57072fafb52119e58ca1516eee808b2a74efc1cb43944b1974e17032d21093d286720396ff7399fb63c1f79cd6a75644f67501af7e6d876bc562174d7fe4eb0434c4cfee35ac6be75ac1820791537c5df12a815ba14d92b0f4fd6d78169fa6451cfff0eac6ffe080c5c2bd37fc059beb2f3793a7cff7025761e181b81ddd54b11acb6032db51a7a8ed713315f3711ea9f0a97830ae8b36494b8543ac9b862745c4215db849ec6a8fc6a1c5ee285c997e54b3c9fa212c4eef18b07de6901258f3ccd0dcf9ae10e0cec6028fc22b8bdd77d9f5f5753eb121e13d18332b7ab5e4e1b8da6ed82e9149a8261093fe0c9ae8dea8f167327c5614e10282a690196332af402f400d795d62b79112c1b39336e2d239a9a003fd684b32c1eabe3d12736033faf849ae7b6d13f44770823917fd825b94292e2cc911cc0bd9f55e29597fc6463429b0e3113ce09bf1223150eff80c462c613876c1afb7506910c9d57bcc4294dbf2154e84ded32aee02db67e7e6d6ef0b310397c49c631a159f637d7b54fbba46993bde91e06229569289dd10aabdca78e37e5e65111201ba9db43a5d736d09002d9bb979ba354884f0ddc0e655817c6eca1bf4e8399e6059ce13bae13442905698486b29df39028c226fa402d682da8022ed12079080cfe05a60c2764aa1d7b8ce819ef89b73474a62a32750b401f56656c401f6fbefb83b3fa391cbd494326e63cc172aac33ea37c2ab915cfa8935f6c1b643657122c82828d974eb995919f27f9045f1bdaed542120f563c340c7534d63bd89254d5aeb0962d72358fddf008461ffb28c7ca3331224a274f4a23db46311983cebb2a0c25c643781c581dd1853a98484e7daa303272696b4c3fc3250899de7b73273f0cb8271959eeba9f47d6f27845142bb5a30241b037f5bf9b9a97dc46a8293e61834ce50f783911f3be4c332cebb2a1254d2d02770490657b1d03830ed3ee8deb26801236387b361c6422b4119a60b45c17936281de0179aec58b237faadaec4581354ac0fe01e42e4aa3e7d9660abff18d87106ec366b781a8e7b6815aceeebca6d1742fa5b320e1bbccd556fcab55f5df0fa25613aa330b14f831113d7b0e58ce43bf10ffbb5922d8037fd6e8895bec323a41dfc55b695ae69ae883e77735dc097ad451c8943fcdb85658d42a356aa45a10d079f8b8125a7b007f583ba170dbace2b0baeb316a85c9992f4cbe7463bf97e420595f7e9889b31842f7bcb87ee9e1659e67146d3eb1738237d580ec4057b010007e11de07d72ef853d011846238a94be1d45b6af5221dbbaff5d7f2b345aac312ee1f0ba76c97dc811aaf550748da69a8e703e30d162f6c4d5e3bfb584709c610de2f1e1324166ce349aae1e7925db177f435d4dea9943c13a1a14583bc4806811ec85e76d455b6ecfe19375719b71801e53989ffd31d68393958d5db8d28f5e23b717a3076b3f25ae79b373b4c2f8b74ea54e68d5b611508ca732b86b8e682d0d2fcf22fb341c462a809a437c34275a2b1e99cd2f306ca0e5c471513ca2636b189a2f7b2fd610eef2f1f564c494ccf64b450345ad58e46520438d478c922304080088b7ac282721cda9cfa89dfda2df6b663c9e071514b42a6e3e3d3f4b8a382289a0b58bdcc4d9e19fa811d6cf64b4a703bd072cd660021d4d515e59e6df6b3046f12a2efae22d170874e2650dcef1a24d6974f77cecf09d6fbfd8b34feeb40652708e4d2e7170b5938e06b95d7239537f7ff192bded21492eb0b817d58b4d165b06c7bf692e0321aa3778a74dcadc77994701581806ecddaadf49f7104e7f0bc5921a00718a37a6af9d199e6e7d94a2bf032e92c0eb564adf2a7fc68f24701231b482e813783b1f35ada11c23433530cc7bb6fb050f5e9ecd90b18268a629098693da7c2767302986f142e06ccea3467126f980182235dd26f84f3b4168402ab0ffa139de9f700d94fded6ef3da03e6137e64131ab66dbefb912545cfd9ec2140d43b2b04494a1cb5de412225c593576471f8421345c6461404437cd50bdc05e52284204951c094ac5183937c52eb1f89d5efc2ddad671cfaee747cbe411027d28a630695a70bd84e6c8f2204baa7816303b598333816bd2e069cdf2d900c25394ac8abc092015c03de91897529f88364c5920c96d79d36adaa49fa4ee79a1acf5eb5cc8c68c2d207e2c00a4be93e9f7f68fbcf3b22641cc46af5b00e468b266253abf616d8a7219eebf4ed0c186804c9aa518e6498a95d27870b09e9a7910d7d2897abb953838358a1c632c883fdf1da7f014a0145b925a4d62389afc2d21d320b72198c16c59816aa7980f35282f8161997479712acfcbf2ae7fc939048221d36c6e5386de18e2e31bc9151bf31caf26bedd9d9a294b1582a0ee1fd54da2b29ef6dbc542e0a402324832ab4a568d02661bee1222aca13a8b8fedf6d1a212c033e3b768116702bbd085431c7df39c570843069c428878590357edc235a0d12653312e04bbf99f98c84e15b59ad2dc98ebfbb620ea25d8800361638225f0f26fa394aef30b30116a967ec0c6a1fd541f82a8091afda6a6d6efeb080c1e0ad26577e4b645ddd36cb9658dae88d305d1fdc55f0015d1b457fa072d4873e8fdb1e81db21d53d8c4cd8c3d14ce58e0a65f6e4f36053c3c0c3243d19046bd8adb80790a527dbc5ede9d8ecdf60de807d0e924b52c138cd1ac03a04d8e88d07c6de29ec091c14d2da49d5d8782121da331473e86bf1bfe45605de793e288bb1aa71066aab84a29256bd7b881523d47ecc983a2aa4255097c5898bb5af93edfa26d5015bb0c6cd798907d7713346c22b48aee272d38248ae714ea7ec5b82561ad2311fd201458f45a4dd86e7a0f108886af9c8d7613617ef0163f18e25aa9ecb792929c4b0889000b9a49cbc9fb16aa12d3cfc54184f17e50144038d3a2a3626f31e2d9fbc165df3f9fcd75d9e99f535022ec91b19bf7c24a033acc8944dbe79ccf224ce84f0ab04273fd6974069d53931a3a69ba7714be5da0f234df46855e054e8299b8a573817dda6b09843956df7a0e2e21d4ff9d91853431e11061482a315f001d434871e69df4e1f24275172d544c130a1c6d40b3bd6b1d7a3d4520217577129eb8a792342940a31d20af3a99f0f4e005da9e561e5237598bed6ee3ee3820a170ba23dcdec74b1b3b461944639305c1596d3b64c83582a758a345624f51be201d7688c69fddc0ff0eceebb2de6d06dfd6cbb7047857b1ff42f680eccd1ac212b73a863059add2efe748ac8971207921c1195f099f9ab6eb43dd55a303e7cf52525392301fc899dfbe7408424a67c509243e5eccd4cfeeeb00b78288506331f06eaa5fd76a09b44f56feaf2d7af4d23b05308b70ccd0c1d5ad8ad1d47b6ed200fc31fa90f5a3102d27b3b1245948561a7259fef448d208a4d87472bb58b647024fa6e040e10f43ebfa76dff924cd8eb7b20dd80bd5e8cf3253fc75ba2921ba21a0ec8828f4e0b8b951041f4e6b816ba41c45a187c8a6434614b24a67aad33d81226a98970af6c1c254981f8fd1d3325244b579ba13038c2e15e670049d7add31e5883b5055d27d3764f707a699a7c34dedbdc2dfa2388b865e81f2f476a9d74033cf6829ede66868d1df503a99b0ab65dcc9ff4581d674e3cc0edd62f6144551a124eabc55b48029f4d416c598c41215d2e3517fc86598aee3d3876d356fea301e68d91f485409a6939dd9136e8ec513ec47d57cfb3f3e0c130160725b15d712c4d57fb83fce70a48b1f42a06f7652f13a403c42e096aeffd06aa22ababfa6f16f60d110b6e2b66ea8828bff298724e5b5fcf493b2106050b85f98ed06491ac17b4c24d508d47a33aeff7a156692b0f923db58fae1b5cc154be223e28cf7085008215ea995ef6c42fc4c9fbd66a6bcb70b2b38709054ea35cd475bb49de59e10956147508ba2e4fb0382e90998b369f03ec4b14d8d41369fbac5544b4139cbc2bd1cb184c504f95bd229f7df496b1e8148eab68ff7a47356fcb4370c00f9a452fad6ce685e37ef2fbe60c09063c8269b6f4a4f58501bcdd6516d59ff13bc307194265fc4a499426ae6b3d0dbbd38289a438005d3a1230768fe7f22a6591d73c1897db709e4dddee2ffea8f9cf5f7505c38eee0e056bb84b077f668fa3fe29441212919a9819f2e3e22e177096168d5e245ef96e5c116cf5c79748d55620b9901ef44b6da68ac3effcce28cf482c7117c35f4bef183e4bbbc96512a7fdbfe0381833ea263ebec528a88e53d624614b2be689b4755ffb1d27f661b4f5a41301b90dbebf58ff3eb42dbd35814ea7a002d1e718e722d9adad207374586887a5224a2e1c83282cc07d6cd0aa8d1c5758ba79462ddc323bc3bbb930373ef1c53b92ed2d96d4d238155532d1953410af79f3d08aa233e2fb256778a04dbdd925e7bdbc182a8858a15071189ed105aa6a222a7a4ffccd9aed35c4d7f7ba647273e2393025bcc927bc2fd289d1e434e8d15d038ecef09f44680b726df17fe0aab28e386d24fa7843e6fa06f472c82dfaca32d85fd582afd60ad817bfb11b3cae843c751e02776d66f8bf51a55acfa283bc6442d185512af1be7154b7a2da03abbd4a60c4127af50b915ec98859d5c684edf56b08cfe66418f2a5889f9b3a87aef425f6950fe98c14c6fe622d1d1c2bc47cfea2b721e97c8dbb1d9cb37577cd019545b71f0285121e51f77094251805dbd8d085ecfe8ccea6723dc062bb3fedf7c50593b0205922a540423bc8c3d5e42569bb2e7247ea76f0336bcbccbd1186dc9ce7482d03da45465c43bd0de56532970355c7ef777b177c7fe6a7532f317a18f136fde2224e443ab209e43f772d8074fdfea3b067c5381c65bb75025ee3f9dee77ce4dc03c743c2c27d44ef52d099e299389080c52600286711e9e1c734dcdebff96c9219ba0fc80d6ea186e6c29dc5c3191ebcddb75f9a6fa142abb71b0eb24ea3fb870d74ad181209cf52fb62d6a0b7c45d9f539812c01d85dee2e3de2131e9fc0e9e2df8e785877d96f7453353cd8e66536472753c38f34c924a11f9bcd3e1df5b24225211c57d2be13d95cd10a32f573bd85de9b70bda007117fd932d6670a44a0206cebe7808618526d6bc0230034e66ce9f7e8f7696a54be414b4caa9cd0855400c5b7787ab19599dffb8d491cab2af3858179c4829812ccfa8950148db8b0925224e70668ec89f91317e477701835166eeac74a894791d8eb76b1baaa550f11216c0fd4163f586a4e16ea766eeeda37ace01ec98c628056398c52e154fb4fa782f2af6aceeebd78d7f15b25ad97c9903deecb86333fab6be68bd8c9ff9facfe4214f0826fa40d3f55a50d102bcdcfa14680605bf36c2d142623c49239b8c22c31977245235b6d40d8c19937dd89007a42e79bc006ca2dbd5457f812fa1f32d9f1029f5a1a4d325d1518f606530e4b5d112c7fe4fd991d78467c24a0487c346a3258eb48dbc8290f47244f0dfd8152f2c2b6c12d1825bd4ef6c35da4a958ad6db192114ee30ab3d73821882eb27c746bd064c8d29a5f970c77faa3b11299f4bde099787744f327baf3422124943d9662f27c114be514e8c81cf618c21a009e2f82331c0ce3f8705e19f585abb4fd7adf1b9718854ad54a8bff4281e2dbeea99271874da9862988c3799ddd9031aaf478420767989fb32ad7577fa16523d27518e0a961ab41d2f1a80097e95d8c03919749c46f05967906ae8cc3ea5de2d4f25d8110ada472dc029c62964550f9ce1372c049f582e33fad78b81872c25d5e7c0521a3f7c7a4873e16b9def6fc5cb6b8bd7d918e64498de20f4de19f6d0c344e526175ab4d28c8c1f400c5b7b8cc871003b1d5cdcd40bb673a700a1edf8281ebc4a0e3721c6515c27adbc7da9925914ebab3eb9dae0cf2200cfbde8e446c38d7bb4295bcad28c370cbdd42eaac458f3ebe5cb7fa2ef98f984babcbfdf06f34b8f65091ee205e13701a1c2ea8a843f5c4b6efd809fcfabead21b10bde2d146aee526215a4fe1159a4d459d3e0016eaf546adab9cdf32bc4379606278bade85a725f7158a13d111c9446a19f6b0bce14bf1651b10bc0123a2a09faaf6919b112dddaa0a0ea3a4ecce723e76ff8831e4cd13d7424c4be81605104116740d58fb086a712e326db739292746d1985c1f9ae8899358e00d533a75efb5d7de1ad2742836322d49011ef90a17d991fcea02ac3274e6b2a9d8ac45e7227708a6ff1b1434586a11db76e8211ddca3020b2850fb31e09acc20b25a225d945840db8f189168aaed18b1ffa314189d1a899b0bde3931abf4178309c5e553cd375db69e22ba0131d8234633d63ff52a14b8ee82edebff56b20612aaa1828f1836d47cd48a889358a31c2ed3975a00706170f68e5c4fcf468d90ef43d57ea3f28b737ead30045fc1be2f085a00fca043219f011f86030f7f33ac46e1f9796a788f6b1c600c0cd7f74d21781ea6582c1001799f2d348f46f6a81f723560d7e920c1c8fcbda35d6dd4560fbcc1e3fe95963b4b591935b7506b55e309523f6ef0372aa53f6f6f39a8fced3033a3b0ff7bf32b9347d397c052d1114065d98e7a107ca39c1ebb71d52d79f101b2e9d8bfa85029b731263a28b39c9f0e9400d3dc6dc1c9328a022a74b704f2000bf3cb1c7232b14fed08b9c620894f038b79fa0dcd99081b21527561ef27b71531fbc148df29328c03a958ae4a00e803eac930ffcffab30209fff34b7d3f79102a1b45d43dee8dcff85bda9102cb809b22e2e10f32f6af247f3cd2b5db032c29d5334d06da44474e388dba32fef58f6f41b64ac8c0ec6cfcd4dc3e0f2673f92e5b3ff63c00f9d5296e18b42d1abd074dac590e7c6e16d339e714a8168fe9dc14582423af2f47b9078fea487c501377206684605bcb5724e6dc63c1e7822abb0855c33f180b5b20ebb2b40c27267d315c4c2bc75dd5cf4a2a5bceadea37ed78153883fb91a526e6f2a716b7139fe0b86b2a8900372f0172f3aaf9ee4a2019ff06e9f15dcfbba8dbb9dd9f3a28e9f54bfd43de7e0f8482a383ed5b608df6cbb6107cbdb223a442c91a27b79c4bebfab136990c609a006f94a4b4aa357bf3b4d70581cb1e022e7beb4fec5b5fb12d0c964ae273eb8ab4dbc0ca9e38768d621b831eb161ad789abe47c91993aafa4250b09fbac7fcb9f489d93a0be0cc4980ac561a170026532a309839237bfa1081eae9a778f6b16665de3237f00c8124fa1fd3253636290e6dbe6eeae74358d67aec6b012104749f53bd345b362b75ab3b549d088599bea97a0c7955ee2b01338bda7d05c5579e6822b583147e1d0958e70eee1b1d6559c7b4a21947d81d6d7f82d511133b9ce051588ce4a1a3e381ab5775ad0a93dbe6e14e6f4d040a4aacb7864a1468b61fde0d2aad5d57f75c6f9679afe9", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed00000000000000000000000000000000d50b7cdf578f4412a219947e71659c0f00000000000000000000000000000000585506e2fc08284102cd3ce553aa726a29012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f17b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e0000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } - }, - "test_exit_code": { - "crisp": 0, - "folded_export": 0, - "enclave_contracts": 0 - } -} diff --git a/circuits/benchmarks/results_secure/integration_summary.json b/circuits/benchmarks/results_secure/integration_summary.json deleted file mode 100644 index 2c13fd863b..0000000000 --- a/circuits/benchmarks/results_secure/integration_summary.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "integration_test": "test_trbfv_actor", - "multithread": { - "rayon_threads": 13, - "max_simultaneous_rayon_tasks": 1, - "cores_available": 14 - }, - "operation_timings": [ - { - "name": "CalculateDecryptionKey", - "avg_seconds": 0.614281708, - "runs": 3, - "total_seconds": 1.842845126 - }, - { - "name": "CalculateDecryptionShare", - "avg_seconds": 2.121472944, - "runs": 3, - "total_seconds": 6.364418832 - }, - { - "name": "CalculateThresholdDecryption", - "avg_seconds": 1.95707425, - "runs": 1, - "total_seconds": 1.95707425 - }, - { - "name": "GenEsiSss", - "avg_seconds": 0.758238652, - "runs": 3, - "total_seconds": 2.274715957 - }, - { - "name": "GenPkShareAndSkSss", - "avg_seconds": 1.242666944, - "runs": 3, - "total_seconds": 3.728000834 - }, - { - "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 18.979502958, - "runs": 1, - "total_seconds": 18.979502958 - }, - { - "name": "ZkDecryptionAggregation", - "avg_seconds": 48.341644417, - "runs": 1, - "total_seconds": 48.341644417 - }, - { - "name": "ZkDkgAggregation", - "avg_seconds": 20.006914333, - "runs": 1, - "total_seconds": 20.006914333 - }, - { - "name": "ZkDkgShareDecryption", - "avg_seconds": 30.277848645, - "runs": 6, - "total_seconds": 181.667091874 - }, - { - "name": "ZkNodeDkgFold", - "avg_seconds": 78.310650236, - "runs": 3, - "total_seconds": 234.931950708 - }, - { - "name": "ZkPkAggregation", - "avg_seconds": 49.050973916, - "runs": 1, - "total_seconds": 49.050973916 - }, - { - "name": "ZkPkBfv", - "avg_seconds": 3.850818819, - "runs": 3, - "total_seconds": 11.552456458 - }, - { - "name": "ZkPkGeneration", - "avg_seconds": 66.056590278, - "runs": 3, - "total_seconds": 198.169770834 - }, - { - "name": "ZkShareComputation", - "avg_seconds": 52.534038875, - "runs": 6, - "total_seconds": 315.204233251 - }, - { - "name": "ZkShareEncryption", - "avg_seconds": 114.608395854, - "runs": 36, - "total_seconds": 4125.90225075 - }, - { - "name": "ZkThresholdShareDecryption", - "avg_seconds": 251.230740403, - "runs": 3, - "total_seconds": 753.69222121 - }, - { - "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.093863888, - "runs": 3, - "total_seconds": 0.281591666 - }, - { - "name": "ZkVerifyShareProofs", - "avg_seconds": 0.264344016, - "runs": 5, - "total_seconds": 1.321720083 - } - ], - "operation_timings_total_seconds": 5975.269377457, - "timings_seconds": [ - { - "label": "Starting trbfv actor test", - "seconds": 0e-9 - }, - { - "label": "Setup completed", - "seconds": 3.273315 - }, - { - "label": "Committee Setup Completed", - "seconds": 20.279577333 - }, - { - "label": "Committee Finalization Complete", - "seconds": 0.007173125 - }, - { - "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 5158.12970425 - }, - { - "label": "E3Request -> PublicKeyAggregated", - "seconds": 5165.210807791 - }, - { - "label": "Application CT Gen", - "seconds": 7.707659625 - }, - { - "label": "Running FHE Application", - "seconds": 0.0710595 - }, - { - "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 835.140242834 - }, - { - "label": "Entire Test", - "seconds": 6031.697821958 - } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000821730b5f6c7306ca00000000000000000000000000000000000000000000000027d5a4288c59517c0000000000000000000000000000000000000000000000061a32d74244b4e4cd0000000000000000000000000000000000000000000000000002752ab748caed000000000000000000000000000000000000000000000005a4f0140d4a1c7f7300000000000000000000000000000000000000000000000512ac80c5fa266d0800000000000000000000000000000000000000000000000d400822869689e0ec0000000000000000000000000000000000000000000000000000a3ecc527c1e200000000000000000000000000000000000000000000000564b7881fe1989f5500000000000000000000000000000000000000000000000674ecde0680d8bf4900000000000000000000000000000000000000000000000584cd67f303aef6c30000000000000000000000000000000000000000000000000000623ff53a7e1000000000000000000000000000000000000000000000000b27d89433be674a5400000000000000000000000000000000000000000000000d03b476bc4cac62c900000000000000000000000000000000000000000000000d562dd63dce28dce600000000000000000000000000000000000000000000000000007014317d4d7c2bdca3a8f64dcf8eb8b35863e69e18fd4f152ed73eba57c25990e80b7c74b79013d6c8d4d7310cd110d6e43bb18fc56336d2e4da3740b50e8523d5f9594bcdd812036507369a09143ca35760ebd0173b2c22e17b4dc2980119e4fbf1ed9cf2a9021fe43ba1b07e24480cdba82eac2af02f2e755eb1b42e866b12ef874db8f56729f0b56939196bb391b63f552db3f3c7ec39a1d778f2686df8f01eacb03019c80d8bbf928d3be121e24970b738be38dd033d077d66095a42d4f1494b44eb7df0074e45e1376a363bcf636637b442460155ece719bb40d1fc476322ceb66ab5261818431b676c3ee34900e20139f2ce04bedc6ee159747b931b42f2b22b3d7198079fe5ff134d61e1d32094cc83972db87162171f68aff7e77845fde2854f3b9a0d21b74b20d8c2ec809d9db9e189051c0dd8557ec1661fa1a1b0ce322590aecc23e610a0c95bf8e2f1ec67fad3b50a0d2e04a267e7844e7e78b710b17602724a2b1fd0daf5aaae67ae6e02faeb646dbe547f0c699e2cb1379f50d28f36ba218a1b615a728d5b46c9da5f17803b7b0ddfaa6af8a8d6d945602cfcfdb1a8c2845e10ef52b08e4afe6f9717aa1865a5df615627ece311d2bfccb7831e0631a7271a08ce5712e16458da4091348c134bbbc0f6c546f14c8d9c231ab7975bccb9e02e0c991572aba8f415ac14e2e466eb01533edbb3a35795beea6327e635c6b103740f2b82ceef34e542ab504d3202e68b230920bc61d94d1cf18cde724d06e23a482b31011ca389f49559a044f8bc99e2eceffadc140cae4937d763106987d7fe30299aea525ef4ba58a7d10fddd62430590152bdd9c1fe3f5dd865bb2ce4e22a65086dd1e3b14c653011e7a3c89c4abaa1ed50be316fd87f42661de510e98dc9bf0dddcf46263bc755e1f5bad01ba6f03708b0bb47ea5aad02d6a9052b1fce2f05226a3e2e74566f10fe938081e85af521d530facec93e917a64abe8a781e246752f984676b56b5a9c17fb37ec862c78c596c128c75422712251c9e25f0fa4352a2a7bf93147adfa3937ec9733a4e035e4a297fbb1e9599d1de9a59b617dc808f117e08e894d7ebdc106e7f065fa3a096cecca51bf047ded0439a19e86d1f4a358099e717b59523313b12586636c72a0028ee88c7cbf053fbe465dc0d546b698cf132800fbde649562ed1d1c65b70ad4ba7497d90bbaa8dab5316435f6473614f501bf945798b6e787cba40d1202870cc74db20387287ff9fffb4cdf7a0160ea04251adcb89e4bed874a70342a96386f8ab23147019c7e3dc8a6f2d0f6ee77ea1b1683337f4dbdcb041c320a5df0f41519161ce81427ee42e7e55b381564cef1ff1036c245bd9b7f0292937e19a58c6f917d8bc00ffb2c098590ffa91e35aa88bf0f29c027c75a7685fecf83028b01891bf2582da15f30dbdadf169772056758b112d80f494136784c5e55d2ffd210cab43b12b3b22efff7fcd159f532727da6ec05f1b98a8978a14a941d2489d0948799285fecc08732988a56e66e352362a6be2cc8b3127ad5279b02a55f57f785b7e1db4de806714cb6cfc421b1af185f408d2beec04fcf9b045a9fe4911bab199445b8cabc822f63eda13db7ea709458a02c27135db9d0e4ba34442524e335b54395b3c5d1d19d856c4a813e0fc2303a45b50eb4e75e5e2bb32813c7346b9c9c1c0ef2b8cccfde4c1c8f4370c89efceb6be328a05d97d39aed2e4cc7d7536b34af28247ede4000fcc7b428147375abfdfb5516e986f5197eb977a865b855e9772fff94c52ffdf99286ee66fc4bd6ff9fa6540e0da9efe06b9e2cb8c70f7bed562275ad9485849152717537abaa41b09aef482b701578723527aa01c63295dddd454473177bfd0415c06d3d4e1848d50a58252bf4553e8afc869321e3d7796ba736c8b389b852bfec26444709e9d423192bd117d4a7cb64bb293e7c7033de3dc675267289c8ba6fcc0ca6ce0b9050e79e4e2d12956851fb28b58225eb8c0ebc9a45158c59a5655fdde45136ce3888dd460ddb15888a69073d825555fcd88eb1a3d7f67a4ebd571c54d2d1274518f65805c2382c12086776a4e11771b7d00b480fb1c5a78c300cd815f8039dc45ea68e61c51e130a50b94972ea631e451d8eb55ce9868d50b4e67952699e020fe4a972b44a36115a8998e760ca80bfb4b64576d97e16bf0d1c9b703a106482cf6872973e94520ce2037c03b5c1cf4892fe2c779650c50d7b88b19a1f6f4be7f5c78fb11783571256a19df3f4b96c9fce648218195e60d1c2e4c662d66ccb18a71bd0a9fb8a8b23c3faf51f7aef24669b70e1a4274abc0f66b911c67f15fd970be027f76d1aab14d557ba7d4f268f95afb5068341ba0900e85cc7d322674c057004d6fa17189123eae8a63d39bfc00d3630a696781bc7ecdc4a6b1d7677ca60c64c32d111d3c10f75c8f14135d4faacb046778787802723936036487091e65721e7feaf94a87d10cac1095e8bd1785f16c5e7f0f5fb9fbfb5faa9918ce2a047c7c0debec9fd5f2c41c01fe8b3062e6fb6e6961c09ab4db1da10ab612f4c5a7328a5209dba00950b62695a4af7c7304b3eacd7d23b4de36c705a55605659d07d7bf79a382923d82ab46fbaff077b68f5b592be6cac56d52f95b7f67123c6ef30cce46bb673b92b0ad6bccfde38ae2deb094a64332f2645f4cde17c0a64aa6cf9b79a7ed828fee42cca7f36d7f6b946dfd1e01db6832cb6f4eab7ea793718651913922d4f360d4b21f257a41c05813fdec7e7d01cd94552d86eae1bced28135690ba7c02f94c4102e5902efa5fcb489bba7b608b977362ed8ef889df360bb12d3f6fa737cc23ea410e2c92ffcb685b442e3884384753e48b3af21056bb1eb92dd975c59aa54ed01161b2d1f8b8e48050ebebc4226dad4c4efd3ccba5c481e80a25e903ed9bdd7992355c18cd5fab093b281f89fbcf4c49a728b3339e0a50b7c8f3eaf38c0a45a241cd2d8c6780c99d0506d6db1d8b009aac9b761cb438e732bebc6c473618f58a526589895742bb0bc9bf2157b0ac83056acd061226b5155c952999f617205e6231dd54dfe603fac9886d0279e7a02a79d39fc16d9590496af4854499544f0d61623b4c14179daa63917368e6315d7c8044998be25f1fff60ff99cebeaf702094322d12ecbd646a3e725894e99bfa905064f065a4bb9ecf069150adf4f6f234c7203c810c6f79c786b859baf6db1ce8395ded182bfce2fd1385b1fae68ce21305026844979fc3f6f15e3f01a641e070dac977d4c4e8251de5b25aeba717b43bcd322a34d4d3c3c7f307f3cbea1f6c4ff7736b1fcbca1b856a1d39a1de2a09e2ff306d5df092ac06ba92c3a7a18fa39a0a90e5b2c1d4d8645f0ffca3717f752afc42eb754439548876e6b43c3a5d94e1bf5abf0e7f9b5a6686050cbba01a352ecc21598da0ad02780190cf24cdcbc430d5aedfc4770abe9c6863cfeef593d95d28b1b050913d0e34830cae0602973282d94fec6205ec8756b4d696623c67d33e55e05d1cce77fdac4c7fdcfaee946a7baed22eb73fb1af5cea27644c13e2e249b6a0fce2f7340ef3aa4e2fa39063e6c8ed748a9d29f33a38f4b50e12d60c03433b81a80e41a565ac3c7013a1e851151847f3bb22ebefd9ff335ff703efe6189df8611dd23fff4444e25494a43eddb53fd61199cd4c1c04def81d6f77187f8f5e3da1a1129ba1faa492ccfa688a8fab7566278f412deabe1f69458de9a4991000314172a67e9eec385ebcc15e24e8cd28e117a2e885e418a5eff75fa6e0af53d4782282f17a9691bb7192b5b670ab7a577b331366962dd0a36346f28b8f56ec241ea0b6552eb0d11d855088e95a118fd271c8a3a1922ea0bfaf0afe9ea5c758e20492391a1721c25d590aeee4199f47b8e5c32ffea485cde82f7392674c9de1aeb3c183f9916e9789575f1b0b461ed9905831051ce8b19952d6c331efbf48b7dbf4614a5b15d0e87af9e76b285fd9ecce3107a983c726ae5d3d6ec6e2baeb34a9e390ee6e7510fc9579db80f9d8ac57e1cc886eaa4d2e4afe8430ff03b6c6f3aec5b2ebfab036e1b2efe026beca2aaeca416a017afbbf80ccbb2e72791f9cdc6f6b505b1dae159a08ee04351f5f2f240edbe45973e734d98f6f4cc9e2b7da8da6a110f7e97eed170300d1f8541ad1e1cb3bf6b3863a4cb490cb5c39e6d4faae2f6ea0082ec6630d36696ea41f90d0d64da3c8ff4facd1cec120e8736509315d426770883f14157bd101f004e3e63407103842843b512eeef44a77743bcefe483286303ab560fa84c14a40002cd93f22ece518b999cb3121c7e1df64eca2069230193028e8d3507fd7d7be0e586300d994405ce6dd9895c747b49f51f20d494c916c808ec85a1e118a4ba5f6448c66d7ebbbe036e81080e7fb9d38cd1ac591d5667dd060cdc04db6517473b7814ad9d757fca4904ebcbc818ca1f22ecf674dcc0c8ec1bb2763dba05f118fcf7500e370108e9004dc46a567294751b7ca51e51229b22220d0d21392e57177d6e2671cf3e954eceaca2cf87918287bfc1f6bf533708943011f277db9e4f9d9ce32ae3f3a1a3343d87d3f95b52d768e4848a92aff7dfed1aead60fddbfb11590d84445c4e75305bb2313c20df2f95eab7e64eb085ee372282e3acefad52cb23bf8855d760a5ded264e7cf806f650248c6db9c54b48e80709642a094184762433ca772b01c277a6fbf700c2046fac1e7c45852dc4c127ec0307dde473f72467a910331f49b44d665488a4c88895342608a78ef07cead2c305ad0fb767e03b49b8e665584ecbb8651dcafc1b5c116b33ba927fa8d15b98f5214a6f4c52b2e56646a8d9cefa2258846c3de653e2297a4051385c199d278a5926a6a8f3f0356900b0f226530e88bb4714c63d444b3dd57e11227770c75d1d2f225236f50e50cf07d29626531dcab3db32b7c1a3af3e43febe274500247334660014e70f3347b1f65c35c55630d4db54848774e2c140eb7c7dcba10652028cc818c7a328564594c77c3ca4d6cf9b0ce46a65cd4cf70a6820eabf65f863291f0f0c89ae9929974dae20ebeb9d518dd20b2795765cf0b0addfc027806a744965d41d9fe3385a89d328e2f8a5dd616160cda986b7d7ab9ec9543d99fd0f70ca38771d00e76228c39527af15a012f227bff39d231a2682a9bd9c4b45ca1fc2b260aa250577f8fa880edd4a07dd8115511301135921bbc68b53bd2375c7635fd9dee70dfc0e14e099d417eb4d6035da9ff7a9f647f84df2437aa344deb5737717058d0fff3c23d38e8d2548ee3ca6dfb23b0c4dfd22cc50c86134291cda45e0131e1917765092935098bb1a016aad380ecf5edc52dd0f95e566d538900955bcacc4342d16f512ab3e6aac573cb9a03082605ae8a4850e455b5d1641c03a37b0cf6ff90215793fd21ee0fc330133beaa6de3b5d59c40755ce8d0cd2c434b9a228fb79815ac089a2735a487f497d5758fa303de789f3ae7a09298147bea45a9c69ce19e2d058fefcd8ca169e2ba7760c96e7af51de2a28cde2d2174ad0a910dbb954f6e2fab7246831353872aec29ee4df899bca2eb5001464c97b1cce8c138fcc94ec40c8e76d15d58b5353e6b3f9ffc5b871ea1d2cc1fc1ab411671eb7d4f80c28b051dc71f34aaceee78f29a56e4529ca51c856472af3c41d587c25a3bb5ddd190062840ac3eb0fe54d3b1f28f63b89977405cd88ca9c73df613c7aaadc6965661c70747b5fc78ce643b6811e78b86b7f68736e2b356a934001d2ea264a89cfbe99a12dc049b6f9ec05d04083fd34b5647bd585f1be04af90aec97ea1e1ee9d42b841d7f31c6fd3853b6c12510c173a710adacac5b23fee0d5586900c83d9711955a1d5d41d21d27fee28b4989409d645d05feda97d3b12b1bcf31e156c75bed1d3d299392602de76f62b9103cce850a2b5a875ef961bd2f58321bb605928c80a122147f4095ff4fc0c4772cc9eb0052f677550724a61d0bf0d328fc6116e13e20ba1a7ab9f9f8506aaf4d953b7c13ada2220f7f6f4695fffeb8a712b5cac9f185ad15a710203071669acc41cfcb817ac9aa6d2ed7c763d9de8b8c7ac7374b0896db120a5f2e718e228bd07965ea30f302417d9f9ab3e01991d4090fd2b0920194ab038128867db1f90f4a79b4586f7602b80324cbacb580b34263c724430b9b91632d8d497956dc8231e14a4d8a202676f3b8a272bb1e26122f025c4f8f78d5c72c21b998aebd00ed34be2d76e4a43006fe81588d6b23eb5bf4c3367285c9a9ef2927a7877682d3af2d5e33c05808479f4521b3a17168e6c97bdbddb7770f93d2f01b08b7b99d331e78b71e84cb19252fb9f9f1ac65e844728db5722e55ab95b9f41edcecb4f31deb949896cd3db5cd0dc2ef15e4ba0eadb9bbe2657b321e467ae20c88064c8c15e19067e28939f8ee13304d7283a98fb49625609483ae02f2a30d160db7d5914120348062db5917327da32d027a758811aa7fd1c21b6a1dd8c75e09d22df3f7027626e95fcb651e8b7559471fcb5ffb9162d550aa82338c51cb0f02c653bb8a2b59c2959f293a4973748870484c05d4ff511fa666604692f73cbe282d4e0e243d6d9ba21e950ac205857f6d41ba6073034f315ef27929d5dba83205f192e83a1e5003de8d1c38c6dcfa2593bba4898090ff610b0890515f9bd44d06d0e8c2d93df6d36626472b3ceba0994cc76cafb660d6a692e7bf0559db1ed02fc0cb1865d7d25f89fb247bfa0e1a3fdae33f97abf5ae7d9e35033050445f73244d228055862735caebee5db8a119a9d8d11a7035555ada83e299f3387abf5f0000dea04c2732cab2470e3e04688d9f1dedd763e036634d28ba9f8e2c5240ca271898be20fb585b49b60139770919a34305e52be70f0d50192770fc02517f652e1e7ce1906dc26a8fbde6939f69b2c0645b8cb4a3954ad2b43918305a27046603a85deac0c770c7e523137f35e3000a0bf96b8c329525f65957437b757c262a2bc6a9c8527c1359d8e598afc781fc2e7ed7501a730554c58d2a552329f99d5c2635fd5fa08d5535ca03c6ea08662a9c52fbc5959b1a998374ed3f624bab2b5b2c33c8f5aeb856586abd239d13286104a2e90e79983c92ed579bfd5576668f7214523b4f53f4bee84986ad86798ec100d603f863f155fdfd2fd2cd72f5676a852c49103b1c0c67f55c4af21fafed6d1d9ed79f863c9807eb055b1777945f8d4f1e0db4f8234739e50bb75bc8fde5af7ab0688ff97d67209fce8a9b39e7b4d05d25548e0d72bccaffb3b604d3b33ce22039ed319792b1fa674d396a6c9786dcac294231be2234a234be2524f2f7dae91c55e58074b0503b24530a0c6e6297f19b0e695dacbd609ce9ca7cc5b2511ba837f4eca002f7102967d35389efb30000cb181f2a3daffd5d7f9915bf8dd1de5d4e7659638e9584d13714469a9ba5d37d7313784f05375e9c5e34ef8219292be23da93ad73869d0bc71516e4393f5a782062975871276d39f2f41d36b81ebbc221648befd2dd8b9ae5610fabeaf54f1947f17dbe4490a05d99f3ac74e0d537673afb9d993294d2aed3e7e293ea0d280ceb019e6f0704f9b34dd1a8da3dc616c365e09b09075d2fef5b1ede4a3bba6df095b1390ce5a31053ffdb83cb05b9209d37592156cb464012744e90748e708378d9c04416b2fd032528cc00e516f1fa6bb6bead7b0d0a69d97e0a14e3b40ada5065b101efc0f1f4cd612b723e18127513fa2b82d5f5cdeea70c60581191b3c7cd81f1cf121cc958060e951b4e6c76f2d557bdddd63f9f51676352d04e0174aa85a900b4a92fc1ddfd06e8006f1d78401e5f7390ace2e425bed97aec36ff7e00d2f6525871edfb56c38063860c618feb09c8748f76f88ea04b6ed77e5421025254d1b2ed05884ee40dd3e9536f678676913dc230188ced021bcb986b6b18b475fe0ff25ca62e1660243f9d9b164881c2398ee00b789bc8c4444d3682420cb869165b30e224323381cb5cc73620a7a96119458441256a6229f2bd621cb86ecc29165db17aeae0846231e6b67942cec4406b326de3bf6da88d906298be18f50763420171db37eb6250875d716390e1399175ee4eec8b0bd26efb179532d87783746b5521a5304ec07e9daa765361558c3187a2073c503c3981ea7992d9c60bcf15ed2832f492d65ba407662a090ab3ef91cfcdf450a588b143f4cbf6fa16010ea41310c305c41056c1e5d407b1eeff592b7fd4f7321aa7e2be12587c37faba2dae3b5050e6c1530a67f22db6c8febe5f34295ef550fc8bbc338a40623dc6065c708338604f699048cd976fdb44878c7b1059cd85f4b91b964b8df3bf9c70f3c2befd9322551cd9f2b50fe2d058916efe06f883fde51721cc8523939a3350133f533f1810524dd023854b3ae2592e3c34de948a860ca68eab2e7515f901ba4ef130450802767ddf18f04412c57ea96eb43689d7148daf50f18621a2a2f4aa76ff98011bb12d62e69c1c4d4a8baae33a8117b84857e317f8562da97c5769136574dccc90706388d13056cff5c7444a3beacfb4110d60cb4a41e70cfcc8de69c72a693c3c22cc4374fcd2cdef089cdfe6a0c2a0e8f8acf27d8e36b650e13a8d51308a25a6829c36bb3cf8ad207df79717b3a3fc45a0dd3b5db984b8293dbaf91f785fe0fcc0c2b38ec1232d147cc42ac3808a34322fbb5f8306303cffb36614394275526e41f473251fd02c680ab57b99de93a2ef421ab91e4426b4c99f3c6d03504de269b275d2cf9faa91d300dc7bbdb3dda6da71347133816be7b051d48a33903efbf4407f90419f1332cc03e9fd8c41e24a26c8f42481b2e9fed3327ce1798263434e51fb358e785403ba67331bfb0f3133ead7225bd117acbf86d18a8703e1d3ebaef2281f87c90eee0cdd19c503c9e39020cf0a6f0793d93dfc23d0167592855aa6516320c766eb5089fe5089ff9c2a5f43622cca3fa745cf3e61a9ace7b7e1c1efe1bdee1c9bf8561b342a2cacb2584c6ae87cbc23ca7f21561483dc48e65ed74d70e36d273946c8af8fbfac07b8067c7537725fb15dd74d6fd8e06d9560d04fe9e0bb9cc66a552723894d500bcc67d9dbbbb2f7def521d214cde49257da5ebba58127ac3ddcf681962da25f22c4452c92bb8643d2d51cd72af07b36ec40933aded2a7ca13c7f42fad892ec2a215c0b050a497585d5acbef59321362e9ebc54f7d7299414c10a9a83fc9beb917a3272cbfffa4e7da3fe84a0f10dabec1f7e5a897e0962a0e842037b4604c054cd61aac3a07fc1c81f3a72e51e2ec990d4db00b9eb14f32933b1cbd476385d2d315eb05da63cf9aa7392e86f8789f1eecdc9aae8fc14541b5d0cf84ceb4c04ef83b692a470b986b75ae8fbd52ed926f7c50a3f0a5c0025385e05933854303b5fdd3eb968d912173130bf0b081ba8ac3f93cdefc9a8143fa61d6377db816b1367c023307d2fab251d5ca6e846a272a4577a7fc82d0901acf57ea102113af63c159c1f5d0d91078ffe6f22541bdec846436cb3729c8126556cf495ef535047d5bdf6cdb5aff1c08ace3e8ba81eecaaee4bf99b80bf98026d93bd1b87cf7eb21a4085647627e2b0b2504884d459721c7b979153b14d161ecf00bf75bf12b4c944c0096897bc8c53526c746135500931ec160fb77114a317c0ab237646eff1345dd26ad855f8d7f7228ad671ffa4793f582b208bcd2dbb018fafa828663cf93e8ebac379ed326fcd502f1b27bffe1912716dec34b4189d2949271b39f8384eaaa45e64fcc800b5f805dcfe8e9986c26d7541bf962d1166044483913e751c75bae08db36edb57973712fba0c80d6813f1e051c2af60ef6e117a84999eb5ede01635927eed50f201635f19e297f45c406e75470fbfe8a40f0db953affc8f61b592b5b0bbf515eede750bc8a0593edcaf5959a5445a1ae0c1130526a514a02445e3a6978f1849f50a7c23db9340d42577892f4d385814e49b151466ff20534e74439acf791d130b0f765f9c9b2ab66fda9098c7771834d71e0ddad7d678af5183635414a7ee104cf52afc1dd2ce8fdff5ba21534f4d73667501ddc7df289a79ef1a336f930cdbe947883fab991b26adf61eb7837d582b8e9d15ec335ab0809126826fb09abb2da87e9aedca01dcf1a662b74cc555fab8b65c013fbe0dff90977f2a74360eb7fb0e75c59408389757e17ad071d76c112ffbc4014b0ca129f864a2489339ed260cf0796380943a2618f3b13e66649fb04104ff15197fafa3b047eff947a782bea063ee95bc5b8cddbc7db60661db0e6a47be4619fe21e5918752a3f48836a6630ac27392a10fe2b6a6b292903709ee2f82c75e0d667d9b51d5ce9ff23fd6c7f2c5a87f087748824f9bb170c33a51a704be5f042a94345fd5fa26e80dcc485aa26fa6c6061d6cafe422caf1eac52f8405a6c0aa2bff08666e2efbca4f1baed7f5532cb579708baeb3555ae6b984fab00610901e1ba452a848424b71e55829122932331f5306b62a5beb24c70237eb9c0e0207591d783524c1a28c1fe61f050b6bc6c300011f151959e387c0125b787cdbd2c7241f837fb79f84889b30cc8e5e84cb49a895585e78ca301052c5df2a4fc873db770367667202002946d7dfbf953f113a006ad4837ea696baa9272c58c9af1d6d8e1f0f21da94147cbaf178fb414d0a77e22c33c3fda3d0be7ebcfb9d95daed750008c9a1d8ddc601df5867b8277024e8c2f447a739164cc63ea44c03ad55282f181cf08f3933494d3938e6937267d1f54289a4e1f19d16abd449ee8372fce0834b212bfd7c33276b09f5ad9221383f3bea85ff25427d5deb17e7a510d4104e32aa160ae05861837210115dca42c986c81002ca19b8d3645d1b473134a160d34ee3261287fd51ed3dd9518c3ca8a42d99bc095a056a3a51411fe822eed25ac7b9392e319e63cfbdfa8c25b357407e9f6e2e249f9a75b5fc17b8940b10c7a8bdc0c80ce7c78c5a7da26bef638318da682b033778c232ce708d33e025782dd268861c06d6ea5cd80d15066128b7b0b5d5b6f51f240ea176d0580ef7b02eee47a5cce712042e07964c9c052cedc831ad39cb143a888041c8aeca71ddda00e103d609160b7ed571aa0f7dc432de1024a9572b2f46029fe8563ff2fe6046cfbc4589099a139ce636ae588318153e65aeb57232762e7be53e82ec3c6f46ee4f923625378f183f5c778c1d9420a1523b518c7b970ba291de12f4ee0d3abc0daa36f7faf05901731bb21c7b0c1e3fa46a76e005632f8eba62e811233000cd2539f44b06619c0391e67d0b33b06bdfc98775458a4293c9515b87e5c25f6cb6d29918837096890ed9428f0cbbdcb8eb0edc385318bb3e61aebb5774cb91852ccceceae70c38cc2056a75e1d63ae67c774a818ad111bf39acba607cbe4864fe74d45aaee92912e237f27cb4f8d40f7029eca2bae4caef946b9549348e37161a6e56168328e03490e955c18c7bf6cb2ed0f8a4c94bc635ba90724e62b29de6d7fcb2249d3951b32012abdacd304147bf0b87a684c56a84e4654a1fdb1c2109d4210f7e9031f3ff9280148dad4fe9dac49cc6815a9d2910e9676cb332990e6564537f5a787e126bb154cfff073cfacd39e74a57c4d0d382155eea6093b96ee0e0bc1d204f58742131e755596903ab9eeb3e947677bf81b62c73f504acb1adc7e4dee30c034ab75790ebae3861e8c094437ac0d5cfefa3b3b0dd75c18b443e46060bb69f122660a7f0d8147ccd093f2aa6b1ad705f3791229891faa6f27122529f42e149eae990fde118d8dd0656f0fa9f5267beb6ccc40fdbbcbf34de723c173979387831ba95cc811e1c79446cb200b31e31f3200a287f582be616f920f3b24590529f1abea7c180a6ce5ab8515f5498960440ddf5000079fa233245d1df8fd688ade85739eda4906703d70e7215b65746facfcfe1aa1ab8c7c817b137661d535ba08aa63aaf44f2b87690dc15322b2c5cb26d729a557bf30748c7dc60c36a9610c9e758330ee4e1efec3393675cfce7c64da0c1fda8ecd235b8c76e3681cff99daf8473c686d1f0bf6aabadc7acc20b88f11869b498b3d839f679d443073ce6ef84512ebed8821286504bf8db57ddaa7f07b0ef748604de3498c5a8be6c3b5b8121515bb8306782ca30bf541db60bd1eaa09ed005970e3c80a2c7c143f6504d10b208ca352b8880e915045e50f5ae5d45f7401b0f70076e20ca6635e89bd950b832ff0aaf0236e206fc268a7f893c058c73030c70f4c7deb51ed388a3bee3aa16534d5319836be14f0addc9788e212172376c5f8cfae54ee2cd8792ae628061ce47cc1a22869761305fc7a5369bbe86b6aacfeb72427f397e40e8b901f5b3e292d7d29160a11bb05979b806f46ca206ada7f1ac05c54c4f49cfadcc39cba3e49e3ccc263ff77c319ceb2c976e8f0ed37f6a73c371cfd76d1b82af0a1e58a6f8e6c0068076db9c229f59e929d9547d2de2db72aa9b89fd37dc41561ab1809614d396d06eb5689f00b058cb17c2b3cf46fc636066b4632a5023dc55731dcc76db3dc1a0a0ec84c900a469a7d17720805a3c2bc7ff493a2df926d49a394a7a0c45c7620f051b2ef14160d19492657bb67d1bef596d29b79d29a4babaa5428795879ef66d3aa7dfd7807592edaf9d568afc173e452e112179d987725eeb41f1182a9f2bf32db42b63b1898abae8d596b33efe7b6607af0079dac5d356bd7cf13d10d0a4e0768f2114c25ce2889ce6c34db05ff06fbfa4b955b018546e27cd0bbfb2bc0cffa0792cb5823bab8a977dfd68e70f068ad03ee38aae0e7d49a8957c3878d643e6d0f65fcec272c725699c16e846c8638f5cec0fa2d981165b3bcaa7602938f4a2149ed71e10cc23a9c5e7d0a3f5461709b23cca9f4eef68bfd06a6fd5e505e756ab4c40a8e2859e0f20337582b5f4556a47c503934f1b8ce4ba7b27a31560d3809029ea2d2067381928499d6943b040cef2cfc40f7025ab51d66d46db1eaf2dba55ffb9d472f04933ae5c2edea96a0468675bdd6c86d3446009068ea089303a1b9d053a8841d98c5b5a491cc1f8e8e260a3655fd3a6b3dbc20beb8e874272a67260b49afb0213e712579743fb62f8ac169ab6500175cc164c2a5caccaa675159759f708016141c4e101152c0e8e55842f9bad78fa3d8c4ed129f91d6450d6c06e85338347f0f6a91a32ba22a390cff1ceb374f5e2b6745e8905f0866029a4c2f9ea7299cf31ea5b0b47ee4d6329b1ba0f1ed46fb0be938a45a2db24560edd0dafb2f1d5526073075b6b2bb62555d44907340a599754fb868566a768b458d6f6b038866a2512c37b7d743fe40490af6e2267fa03f03e2a8f9b6d95650221eaf0b4c87ccf6d505125e1dcd88c3be809632f750178fb8b6bae40b12c686a256feeb481b20ec7a2024b45fd5c00f5608c9cca96508b2425fa584455c1c79dea9c947c13e54f2bd0b77f70ae6e1aadb54cd3af5dcbd773c96a36c60a6e94d41f051284397b36e4102431a5c1227d7a2e5eb432dcc11f0fd5884c17eecfe66a1ce2a22b0419b751d117401dabda490ef21888c13c136e09941774ff0b070e9b24e762ea7ff7684822e8d26105cc2ca0b833708f06574954f6b582e8053824a60dbf179e50db6f55c069244550b03c11e5cd22653891fe5064198a330115698a1d852f57c32eb855423c9db7c5aa271f155577ae09f6e5a7e32c2aff1f5150feba43b795cc2584c07001cb655353eec53d999060ef4380640747f65e57196198444330db72f8081f5202a4f8e4f598610629f96fdfd5734ff55a7f4031c8538f9fe80d1dfb973e8d61beb98f1f854d9783b319368ae6f9a015484bbad829d30cca00981f840ec563a2e24c11cfd9977d1f787fb0a395ba4b1acfdbf6f2bafee57074e5328affeb76204ba93ee5b82ed1dded622a843db90a4d19c0b4ef46867161b3f6525493b3a3e285408073fd97e2f53fdb86c06e9845e8d410462857a2b9243b348ad17b662cf06686483db4b559d400f33726b232420a522d3a4a63376d60b56f5ee7cac6688203272656b35c2a109ed301e8a298fb397d7b66d22b4867b9806269b28a5c16d2c366e20f6c78ecef995bf41e82d6fc7cc578e293bd917b60d9bd6d1dedd21bd12b872b9d3e443055b534b753d81d39377c29f810995cf6238b69779a358fb6208eadcd48848b22b262ad65f2682172834b865f293d2fc5cc95538f242b21cd12261023dcc253992cbc1c548d996c675b14dac9b530fcc0bf78fb90bad6027e52502201fd2f781fa9dcf4da5c91b6706f2eda965b330521737df12d13d350c2506a779924f2bfd8c634cd73c84811da8534e0b14ee42996d6ed01f879d4d858605adbb356eec9115a7e0a689e0de4c27be333631fcfa9fec8d02744e206dde6110f97142186a44a6f4f220b1295b4e1b0385b0826984a145ac28bcf89a9c3627084ef803b0cae77c7eb29fe2d6ff15e3762928f2f1100f4a8b59adeb81a445e70c54d7b8453eb39ec2e05e612d1c0884eab9b6d8792606e5ae8627d0f4d44beb2d4306f31b6cdc617da0f5374ae79900cd9b9f693debb14d59889d5b802fb8471c0a414faa8abb86d379cf24a883de065656be4205dd39718b1dab3ef93ba2e5107d2c79ae5b564bdaf2971a6ea04febbcf5e2c7b3e3743d8b37631ed2ebfc2a116cc9f8b27037cb4d7bd4078692ed74caa2a1ae3458d1eaef27cf48870bd95a", - "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c91109676183000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008f26088c208eab68279f6f45f234d271000000000000000000000000000000008ec0f15e98d9058c0e708be73acd2b9d2197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f2a318f2fc85748ea90c49625631654288c77791d8dd0792e201c748ca9863ac417b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e2bad40c7a2fc975f2e4811607e4d5fb72361dd1af1bb2364be799270aab5c6f623db21355d312ad82ff6334e39518d7aec226005c56d124daf506a44749cf370" - }, - "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000006443e4dd7cafbc6fc000000000000000000000000000000000000000000000009a5b11556957a2fac00000000000000000000000000000000000000000000000481d8c87ec8d9e44400000000000000000000000000000000000000000000000000027e3c69ce5a8800000000000000000000000000000000000000000000000c89919183396d2ee500000000000000000000000000000000000000000000000156b5f6351b4e1677000000000000000000000000000000000000000000000003fa278d2c3f3cc83b000000000000000000000000000000000000000000000000000257547a6577fe00000000000000000000000000000000000000000000000a7acecc082a3669e1000000000000000000000000000000000000000000000004e4ee60a9610ccb0100000000000000000000000000000000000000000000000139a7b0dc64569eb80000000000000000000000000000000000000000000000000002797aa349525b000000000000000000000000000000000000000000000000e4efb10408e5e22300000000000000000000000000000000000000000000000565dff2497b05203c00000000000000000000000000000000000000000000000c3ee606bf31b8fa98000000000000000000000000000000000000000000000000000144da9a140bb701dfc1ca1fdf6f4d4992fcf7bebfab3317fc2caf844b02fb8ac4436d5adea45f18531115019e88ab0ae6f13233c1014519feb8d5364b2b488e9d836182eba9d800f3b9f16b517a82e074902c3c99bf34e7be3eafddb913ebb2c8d05595a159bb2a543541d15947ed4f1c1620e84108ea3ec32503ad7d073162c30b94474e387c0cdcb845177847e28da58f2486d85540c5484ae86f2294cb46b4cb84d0aa3609083d483a8b920f898636ed7798e980729ee4ba163dbecdce8b02670604ec9b920e6c4aa8dca3ce558f7cec8fb7f4bfa1b0d4371b18bc835220613a1409fa080f120c539f947fdfeabe4f2a16449a964098c614d6fc20e3e80d0403f0241f5d8a13147d749da88cd2a0d8266d4380e5070e0fcf87e2e312a3cdb01409afb8ce082549d88678d5f866e68a4d0281b41a6b539a157e18a18aa47a9d801ba82c15b4285ed10e2b52a839fd1fb5a55f8de22855f4af96518bcaccc2dbce494a296ebe26360d30cd1d486f49978b029766d49825f3c54d91bd255e22e279a69caf0742070495855195395e1742793b60d1378c09dc9c7053216512cbd2452209ec4be4071c5a521b696e975b1060278c4f77f59bb6ef505a7223d5245c2faccf2d7a7b17bc6a08396d8036223bd5f0ae7ed2f2891a3c4e8846b79bd36ade7b09fd6627114aa182ae1731eb6b9cf19b8b841c3c2b6d8056888915d6f3833646d375991e22759e6ca02fd6241ed37565eeabe5d1c1ed7734680293580c5485cae983d60911a42e17e7646d7d5bf1b3633d1bf857ea6d0d6dd6ac3947070d93be4e391a921af9366211aa3384aeeca7a871208ff44293b9bc62fc7a31c346b3bc0a41d8ec2faba055c65299d4680c399879ad3b09b91ca75c154614e7c2365c05d9e1e1bf0312970f67ebf20490f3b18aa7c84bfbf44044a62091158a689ffb7eff443a0d006f17dce991bb9008594904f9599b39252e57e6a7ab50656c9e5a8df72b58d51b7c9f4435e6f52a478dad1fa0f66a4117c91477773fef118f1a5b2e2246a83126b004cc496b97eaeb0939d7fe1fe9df4c45268c13f7acd87f8d7a7251397537066d564a275044ff3e9013c7416550403fd3b89f31931a1c9bc4c78890d6039319c12ba5bf473841715e3058c08b58f6c885980ccb7196c78fbab8bfc448f50b23e59de65950bb9e7523d7c99364b96ea7e2b23207d02a56f9a6ba47ffe607b0110784790f007d11c1a16dfc27e06e1ca443bf4740ab87fe60bd2e4719005c8706dac42041fbc232faedc6e10c6a6f75b3fbc206b8f7c1648c1104fa3deecec91ef7ed44600c3779a8af114f9e25af821788f45bd741c89b36e53c6e5e6e983e23f372c640298eb2ca3865a651b2add2714020d6de218a78a66e53d5eb9c037921c9f13da3795f03f8a780a09119c28b795e6e6b97d67250bc77c40a81ca7253285c8d29c99aecf5b4aec5316f45c7934a7c13e79ba5f526153174eac6f0fa7f1d5423429dd2204d5a0749d4f1d72ded1463077de54e2914170e854d559814782791a449f5831160a02643aab224d9ccadbd7c12366cbe5895233711003e08701b69ae17495eb9c96cb4741fa95d3a2342926bdd5a796de050b055178f9e91ba2582ad1dda122e9168349f910fba42f2389be4035b6a4a0776ec380eab4a7e1818da3933777a20728cd32e8aeb3f6dc1c954b5e7ee588996ffb14ff769561b1427200b52b53eb0c2aeeed7d7ddea931eb9dcd23c5652b768cf3fbefce53500b8030754c5c1ca1108f3ed46afb942bfc01b032b26a8ba44e62c0365ca4033b1b219a80f8a2ca3ff9ad952bbfb2493969d7eaa90cc4aadbcbf36dc76a0c848bbed2978369892c56259b660b0a07ee28fba8b592f129d029bb31b80535d8003742403319cbb781f5e4270e2aac914ced8a3fbbaaa3d9f1e834bad9b9f07b44814681c3e7cf0859f312e121eee65169a441b1dadc576870842c917a36a744b4f4da91c33208fd45e9c462ef67ea2f432b555a46ded01dc8ac89fafc61142efa714650817710edcc68e99b475b7ab403a6de405f94656a27d39fe369e561b26a5457c2921ad7d3ef554ad5330d553e322f95d5c40c50efce4a009493e71875eb2182d0f82765a780330127718248a51508491c0d2bd80766fc4007dc818073c337f882d78d25f805d23f12d1db526fca5ffb5be0e54a6ec25e46bbda65b6bf5aec648184761fa18ff033b31c97d42fd3a43ff3f7ecaea4d304d336e0e917a0d2e4425050be6d7fb830cf4562eb87b213a5d6a0dfe7b3d63f8e11e3b1efa9411183811071ad9e3d533211eb93739574879ef787af1314cbf7fb4e874c3ce87ebf8c3f60f93f0b8ef56a6f27811b193c26971d3e773e76e68c5daf56140ba20f67be6242773bd78452b12738a0ec3b6204e93c9377acc0358971d74ac9bfeaf9245d9421a7d20b5a4a2ecd40e4d23d71ca5a7e131b932acc89bfbd6866b38719ce78d212cf028cef1628129d642240f4b2730357494ac27446f5e56c0978e0c2fc239ee112666f0bee00ed47f0164d22a5215d5db82f3ab6cf249bb50703d3dc27d71423028a4f72123edc079901ef7ccb56bef61f6591b0477b23cb07795773f20e2bb12e5e0ef459fea65851cfcc4b387fc42cd5e71bc089a7f4381c913855a4f2f1f0106cae843799f89fa4b279a364f633a37e4b2640c69857e61279dd803ec5c6d039606c9c10584fac7c1d5b10de7b25738d89fc5569329afd79e885fdb953cad2cc05964e713e4c71e4c07ff1cd10c406ff55cbd851532a4839738d7227ed257241f748badd25148e8ca89366948ebeaee84d613987f693e0d213aaa594db3021fe702e566a8ad2d75ae4f812859398669a51eabc475fa34a0d77bb9611932a904ba84084825294979e0ede81171919e8bbab7354cd01123a8d61d9548b311ce05d765d5f5322a49e084b9796079b3196f5a5ef6761db7007dac93649c4a387d02a34ad68acf136903543014ec4b3d4a3fe900211ea22ea18eae5100b319d329034f88cb7f95f41dded0a0ad75b68c71278a61b51c19c035158fbb8259d36bd505735e825c6b8f845e8845155cb93ba122d3e98c3db8da44666609cf7192ff0d15d728de03a78427d9b166b5ad8555e4b65afb3244bd53993eb496c5cd50833d22341df3b6674d72bb8271c4b82e7f03740f15b18a7ce3fc8943f81f826fc8fd227bf5ff41b0989a237271e140ec6ba99a0c2c913033c1dfa00f9613114733a42111810a91eebbb5bea13246518438196d97dabf006219b2203cbaf73568d7f8077e01b07756ea1d188ab0a2d7d2b901634618048e281da916256acd484950b7175c097adab59b620eb4718fc51493f0566c9e30124ed27baae31fcb691fce092acd8a3e8ea251794afe8d35d55ad7037ee89476c111e04610f219a1aee124c22ac4339cb28af586a14d986b328dcbc25636df3bd150a77cea7298745cb7fd6b2ef63e7413dcaf3505f98d6569f1e4c8f3d5082903c2a15cacd73e75fd8df54e2307f9e71217ebbcf22bc61abcb8690a22409214cd7847da52e761f7ced049a31c763c92153a0b2904739905c9cb26452f4f573ea8f9a7740b72e854a50ec596115078a8b11c57010a27a043652ff97f51ca448f30614810e3765b8a4149a63e291d1683a4b9a0b964f6ffece7dfb0cdf1c4ce4f0fc04be99672261f50284503148cb9351ff4662a49ec4803560cfbc9165ad379fc90ce53beb5ab320807a8d91d0a10934a2b9e4363934b89c786d314b1a242e9128fccfa473dd4fa8f8496710aa112f31617661961f5e97fd2c3fea061bbfb54946e7687ec43dec8861421bc140c7c1bdb0a3d9b5909cab572d3434a2431c3236a57baefb073fd1bc6d6d1912532702a84570676a2eb0d0047ac4b2bc9fbe5c927e3dceb3c5c249d3cadcbe929008bdca758870e554bc839114d0ba80cca1fca48d28166f949e52c54e6928b1b9605ffb5f5f6b3db23931f7e59b994d47f7c474a3564aeb179664ee01f91770839111d61a992bd1ce5fe02631dddf3d4ad212e79a3aa20c635f97ea8d6db1a1bcf6ec685335f5a88413279438d62e691fc5848efb92ad7dbf44ea4e6f23a8d20414139bc656a727e0ebe69c8a9dc3802d314028e5a8643622b705ae5ae82e01e2578c9d36a252ed8138a7aacac14849121aaba10a66d6ddde63431e9bae2151950ccdf9487c6874ba926706e38df897c4ca3183893c211c3846182cd2588da2d398a58df3e979a559c498fe06a4a7a0163ee286354b8da07da8b9eef9945c618800d7ddab549c895cc7e2ff3000f1781554884bfeecb44e4124d603ebc19b829730a60168980a8fa287fc8f86c0a8a90ab8f38e71f804bba01d8365d1255930ccb79be29beba86407961ab765e979b3dafc32bb1f393d16702fbc3566aedda089832173ca5a7e5069fad1e0660959d49e0bfefd74d2314b393f2fcccaf682a22bea10fe8179d3ba752e8044853c47f4683b4310914f61e58b7a1df42504a74113e6facddec9a9ae5b220a1d6cf4fc0415e9f725e11104af98a7018d5dbf4b5132e8a07a7b4803847e32293d13af6a33d297cf15e40db4bade0a4774ac86b0b12fbacf70560a28f3f7b4db8fa73364a46bb2eb57964005bd8e58c2e61eefb861e701eadc30caea24196fdfaff46b16dc10ecaea90bfdcda36bd80ec0f006d9d2e1bcc89a79ee552a9d1d32fa60bc21ea126a76871ff91f479eebbf887884f360274da76723b26de1d7a5e8ce9ee53c7bfe8b5f29ec186a407e75ca0e3d661862b867f5fe4b79871eed93603d868ecbefd6a9b1188007d91ab5aa9944fde3f6b18da6194ed89cde701078d634b6c54192dd528fa637977e5c3675f4ada2b502e13be091100049adcf52752f514bf95c3fe99e4e20abd509446b5fc66a8857e1d2001066787b655dfe8931150d5648b382f60bbe72d6e66c73e3e6529d54dcf4811cdcc0314c615042bc66bf5173485c9881e9544493687da1731b6293a9679fb04b9622e9b578c81c7608e01f3053cef3f632d4ef5a58f172e8bf12259f6d5f21e25a74e2fa134a379f6308d57c51335552484fb0e88867c9d243bc181a090cb031f58e0fa9f655929adfa77a05baa4894c7de6a6d37290d3eb16283ff637dbd2fdb3500df9f2418aa9a9f250fde66f091fb6ef5acdb61a27902e22fa6d5fdf0057d28171834d980f73ca8ccbc0b303a5062a068e03e7ecfea78e72f78bf920d00be36994a8ee4f42a36ed3a223fba9826a967f946e75760a85a595015b8af2e24ad7c59e99b87f60a5fc7c76e351614576c43732e32d314ddbb85daa881eeed1b150a822d9945663292ce60728bbac00dc5ba3c649e814005a52df5c0fc59b81011cfe6bed95a7ff1808bf7c6e1984dcfba5975ca1768a00381f2974a32d1151e75c2894ff4581c65de16e354ddf938c056c93a50c61579815a9f0282488c76145951a635f929a71f8e662224c9364e12748d715cbc7bc93c5606f07687844222416ad582df9b9091bb790236a28d090291602b43b80c146036ae1d2d3c911123f37a291933fab7e30deea52324ab9ab3adb486903c9960992b5ad8cf6d89670e608c9b4061890a793116ccb7fbce38857e020824d1db42f14fc651164d22a60aaa8f2ad0f20b63778277b1f641d7fc5d12dae47ddfcb2d55e6b870d8b2814c20ffc73af5fadee89dc3b3cd116e881d5c768520e9fca681290ab5c58ead371022b65ed2116f353e440fdbe5d2fdf3f025e2a6d2e9d3e94c9293ceca8086b0de285a7305750eb30cacef7b5469a0797e42080970c0c3e70f990892b6ebff1044023dd8d11b77a419b3aa19d4bdbfabaac8d553dc042fe6c4555b6a14fd64ec980146ddd60ce32d76980b01b71eedf0bdee8b18f198cc0255d73108f17b509bc13025bafa936bc61579f8c1a9930017d1f0b042c5bb0566743b6c731e05e3bd7d1cef12b035210c3ecf81d2caaa99da32fea59317e383b970e419f861ae2573fd2b3c6b4f0c1ca9dcd2203e4b1be938e7838f970bbbfe78707468e381083605301c190b6c5f06802b1dc8b2280acaab86ac5046231ef1060033cc35ee76fe2baf2d661ab0c19ad8987ab3cbb89c97f3c6368ff099e09d1febe8e7f6ecc1479f972b5c27cc320c875b5f129f3b6ff24b4c8a2eb82af590659ba225f3e190fa4e6b1008897acc6e797f4357055c26eeec55360f23d1da9543b6ca7bb34519652626273e877efaf30df25e18578770476876265c499564d49f34e9f6a51e30b1dabe0c32c7acc142ddde363e9e60696c0fab4def3165b7cf5799816352fd5147cbbd053810b943836eae47e715380acaafecdaa5c56f78ad25cd6beb48b30b71e97224546462e575474e305a384c11241bfb4922692f7ada05ae8ffe3eb5a61f4a432346ea3071cda58fee452fbc4db70cd573ef7d17106659050ca1418bbd2681da0af56fd4cc9e2c6418372609222d7387c272a336e2b34a001899e647750744331b117ae4529b0938e4e4712dc5686b6caa250480220d3e94a35b9632e3a73fcd126572f8c3d4ca698b890cd2cec074e5e7a8a7f6f624f385cd36b9d21144013e1d4d49e7dadd46d3bb09ef8d4358a93c905259d943a18b6a020e9701b77274ef12ca1ec8b6d0d112d3b1dd49f29c7bc13ee5e7ce00c1e5ecce92a8c5b0b5b464094dae99c3561ef02693568d05c04104963444dbfbe0e6cd804ab19753689d2f1254bc80d6a1dde41cae49945131b625cbd58bdcf37f4cf699f10aaa7267e1d62acaedd0bb43e6003a32c7112ec16756df55e6b945e730898cfcbaddc25621cb0f3ea6045ecd0c18e3c46ceeee924bcd8b5931869ec38afe8b79b6dcec6e649f26fe612ab89de39ad9fb9d355f67b6608ea70697d9f7125cfa4831a71b894a0a0bf5eaba34f2e38a421e11ed8e30af9a02bac691e911dfd2c5cc8bab6fdcb23904e79827c342bca3644d0ff0be3bb71ebeddb24e04066ab1b9fbb82705a3266a1b742992abbea7a4b968075d5348ff4e1068b01de9997679713f13c50b558d69224e93ff28adec2874a0f0131ed5937a9a84f1a1d27815ef6d4412936fb2d9ab2c9f461e5eb9aaf0b937abf2c25c62926c3052eebbc13a311af28dc949296b271595a279367d738f840aab4617ae5d46cf23b6a6f6b932ffc79d71daf27bccb2128f76718008bc6be9dcdb3c0b25b5172b1da3752ebfd6f9ea2c8c609d906b690b01c120df01729bd22d73b94dd8afa3b34ecbe74d60ea6d449c60645f1528622ff8a547dac1025fa5fce863dddc98f0b30fad91b413d57072fafb52119e58ca1516eee808b2a74efc1cb43944b1974e17032d21093d286720396ff7399fb63c1f79cd6a75644f67501af7e6d876bc562174d7fe4eb0434c4cfee35ac6be75ac1820791537c5df12a815ba14d92b0f4fd6d78169fa6451cfff0eac6ffe080c5c2bd37fc059beb2f3793a7cff7025761e181b81ddd54b11acb6032db51a7a8ed713315f3711ea9f0a97830ae8b36494b8543ac9b862745c4215db849ec6a8fc6a1c5ee285c997e54b3c9fa212c4eef18b07de6901258f3ccd0dcf9ae10e0cec6028fc22b8bdd77d9f5f5753eb121e13d18332b7ab5e4e1b8da6ed82e9149a8261093fe0c9ae8dea8f167327c5614e10282a690196332af402f400d795d62b79112c1b39336e2d239a9a003fd684b32c1eabe3d12736033faf849ae7b6d13f44770823917fd825b94292e2cc911cc0bd9f55e29597fc6463429b0e3113ce09bf1223150eff80c462c613876c1afb7506910c9d57bcc4294dbf2154e84ded32aee02db67e7e6d6ef0b310397c49c631a159f637d7b54fbba46993bde91e06229569289dd10aabdca78e37e5e65111201ba9db43a5d736d09002d9bb979ba354884f0ddc0e655817c6eca1bf4e8399e6059ce13bae13442905698486b29df39028c226fa402d682da8022ed12079080cfe05a60c2764aa1d7b8ce819ef89b73474a62a32750b401f56656c401f6fbefb83b3fa391cbd494326e63cc172aac33ea37c2ab915cfa8935f6c1b643657122c82828d974eb995919f27f9045f1bdaed542120f563c340c7534d63bd89254d5aeb0962d72358fddf008461ffb28c7ca3331224a274f4a23db46311983cebb2a0c25c643781c581dd1853a98484e7daa303272696b4c3fc3250899de7b73273f0cb8271959eeba9f47d6f27845142bb5a30241b037f5bf9b9a97dc46a8293e61834ce50f783911f3be4c332cebb2a1254d2d02770490657b1d03830ed3ee8deb26801236387b361c6422b4119a60b45c17936281de0179aec58b237faadaec4581354ac0fe01e42e4aa3e7d9660abff18d87106ec366b781a8e7b6815aceeebca6d1742fa5b320e1bbccd556fcab55f5df0fa25613aa330b14f831113d7b0e58ce43bf10ffbb5922d8037fd6e8895bec323a41dfc55b695ae69ae883e77735dc097ad451c8943fcdb85658d42a356aa45a10d079f8b8125a7b007f583ba170dbace2b0baeb316a85c9992f4cbe7463bf97e420595f7e9889b31842f7bcb87ee9e1659e67146d3eb1738237d580ec4057b010007e11de07d72ef853d011846238a94be1d45b6af5221dbbaff5d7f2b345aac312ee1f0ba76c97dc811aaf550748da69a8e703e30d162f6c4d5e3bfb584709c610de2f1e1324166ce349aae1e7925db177f435d4dea9943c13a1a14583bc4806811ec85e76d455b6ecfe19375719b71801e53989ffd31d68393958d5db8d28f5e23b717a3076b3f25ae79b373b4c2f8b74ea54e68d5b611508ca732b86b8e682d0d2fcf22fb341c462a809a437c34275a2b1e99cd2f306ca0e5c471513ca2636b189a2f7b2fd610eef2f1f564c494ccf64b450345ad58e46520438d478c922304080088b7ac282721cda9cfa89dfda2df6b663c9e071514b42a6e3e3d3f4b8a382289a0b58bdcc4d9e19fa811d6cf64b4a703bd072cd660021d4d515e59e6df6b3046f12a2efae22d170874e2650dcef1a24d6974f77cecf09d6fbfd8b34feeb40652708e4d2e7170b5938e06b95d7239537f7ff192bded21492eb0b817d58b4d165b06c7bf692e0321aa3778a74dcadc77994701581806ecddaadf49f7104e7f0bc5921a00718a37a6af9d199e6e7d94a2bf032e92c0eb564adf2a7fc68f24701231b482e813783b1f35ada11c23433530cc7bb6fb050f5e9ecd90b18268a629098693da7c2767302986f142e06ccea3467126f980182235dd26f84f3b4168402ab0ffa139de9f700d94fded6ef3da03e6137e64131ab66dbefb912545cfd9ec2140d43b2b04494a1cb5de412225c593576471f8421345c6461404437cd50bdc05e52284204951c094ac5183937c52eb1f89d5efc2ddad671cfaee747cbe411027d28a630695a70bd84e6c8f2204baa7816303b598333816bd2e069cdf2d900c25394ac8abc092015c03de91897529f88364c5920c96d79d36adaa49fa4ee79a1acf5eb5cc8c68c2d207e2c00a4be93e9f7f68fbcf3b22641cc46af5b00e468b266253abf616d8a7219eebf4ed0c186804c9aa518e6498a95d27870b09e9a7910d7d2897abb953838358a1c632c883fdf1da7f014a0145b925a4d62389afc2d21d320b72198c16c59816aa7980f35282f8161997479712acfcbf2ae7fc939048221d36c6e5386de18e2e31bc9151bf31caf26bedd9d9a294b1582a0ee1fd54da2b29ef6dbc542e0a402324832ab4a568d02661bee1222aca13a8b8fedf6d1a212c033e3b768116702bbd085431c7df39c570843069c428878590357edc235a0d12653312e04bbf99f98c84e15b59ad2dc98ebfbb620ea25d8800361638225f0f26fa394aef30b30116a967ec0c6a1fd541f82a8091afda6a6d6efeb080c1e0ad26577e4b645ddd36cb9658dae88d305d1fdc55f0015d1b457fa072d4873e8fdb1e81db21d53d8c4cd8c3d14ce58e0a65f6e4f36053c3c0c3243d19046bd8adb80790a527dbc5ede9d8ecdf60de807d0e924b52c138cd1ac03a04d8e88d07c6de29ec091c14d2da49d5d8782121da331473e86bf1bfe45605de793e288bb1aa71066aab84a29256bd7b881523d47ecc983a2aa4255097c5898bb5af93edfa26d5015bb0c6cd798907d7713346c22b48aee272d38248ae714ea7ec5b82561ad2311fd201458f45a4dd86e7a0f108886af9c8d7613617ef0163f18e25aa9ecb792929c4b0889000b9a49cbc9fb16aa12d3cfc54184f17e50144038d3a2a3626f31e2d9fbc165df3f9fcd75d9e99f535022ec91b19bf7c24a033acc8944dbe79ccf224ce84f0ab04273fd6974069d53931a3a69ba7714be5da0f234df46855e054e8299b8a573817dda6b09843956df7a0e2e21d4ff9d91853431e11061482a315f001d434871e69df4e1f24275172d544c130a1c6d40b3bd6b1d7a3d4520217577129eb8a792342940a31d20af3a99f0f4e005da9e561e5237598bed6ee3ee3820a170ba23dcdec74b1b3b461944639305c1596d3b64c83582a758a345624f51be201d7688c69fddc0ff0eceebb2de6d06dfd6cbb7047857b1ff42f680eccd1ac212b73a863059add2efe748ac8971207921c1195f099f9ab6eb43dd55a303e7cf52525392301fc899dfbe7408424a67c509243e5eccd4cfeeeb00b78288506331f06eaa5fd76a09b44f56feaf2d7af4d23b05308b70ccd0c1d5ad8ad1d47b6ed200fc31fa90f5a3102d27b3b1245948561a7259fef448d208a4d87472bb58b647024fa6e040e10f43ebfa76dff924cd8eb7b20dd80bd5e8cf3253fc75ba2921ba21a0ec8828f4e0b8b951041f4e6b816ba41c45a187c8a6434614b24a67aad33d81226a98970af6c1c254981f8fd1d3325244b579ba13038c2e15e670049d7add31e5883b5055d27d3764f707a699a7c34dedbdc2dfa2388b865e81f2f476a9d74033cf6829ede66868d1df503a99b0ab65dcc9ff4581d674e3cc0edd62f6144551a124eabc55b48029f4d416c598c41215d2e3517fc86598aee3d3876d356fea301e68d91f485409a6939dd9136e8ec513ec47d57cfb3f3e0c130160725b15d712c4d57fb83fce70a48b1f42a06f7652f13a403c42e096aeffd06aa22ababfa6f16f60d110b6e2b66ea8828bff298724e5b5fcf493b2106050b85f98ed06491ac17b4c24d508d47a33aeff7a156692b0f923db58fae1b5cc154be223e28cf7085008215ea995ef6c42fc4c9fbd66a6bcb70b2b38709054ea35cd475bb49de59e10956147508ba2e4fb0382e90998b369f03ec4b14d8d41369fbac5544b4139cbc2bd1cb184c504f95bd229f7df496b1e8148eab68ff7a47356fcb4370c00f9a452fad6ce685e37ef2fbe60c09063c8269b6f4a4f58501bcdd6516d59ff13bc307194265fc4a499426ae6b3d0dbbd38289a438005d3a1230768fe7f22a6591d73c1897db709e4dddee2ffea8f9cf5f7505c38eee0e056bb84b077f668fa3fe29441212919a9819f2e3e22e177096168d5e245ef96e5c116cf5c79748d55620b9901ef44b6da68ac3effcce28cf482c7117c35f4bef183e4bbbc96512a7fdbfe0381833ea263ebec528a88e53d624614b2be689b4755ffb1d27f661b4f5a41301b90dbebf58ff3eb42dbd35814ea7a002d1e718e722d9adad207374586887a5224a2e1c83282cc07d6cd0aa8d1c5758ba79462ddc323bc3bbb930373ef1c53b92ed2d96d4d238155532d1953410af79f3d08aa233e2fb256778a04dbdd925e7bdbc182a8858a15071189ed105aa6a222a7a4ffccd9aed35c4d7f7ba647273e2393025bcc927bc2fd289d1e434e8d15d038ecef09f44680b726df17fe0aab28e386d24fa7843e6fa06f472c82dfaca32d85fd582afd60ad817bfb11b3cae843c751e02776d66f8bf51a55acfa283bc6442d185512af1be7154b7a2da03abbd4a60c4127af50b915ec98859d5c684edf56b08cfe66418f2a5889f9b3a87aef425f6950fe98c14c6fe622d1d1c2bc47cfea2b721e97c8dbb1d9cb37577cd019545b71f0285121e51f77094251805dbd8d085ecfe8ccea6723dc062bb3fedf7c50593b0205922a540423bc8c3d5e42569bb2e7247ea76f0336bcbccbd1186dc9ce7482d03da45465c43bd0de56532970355c7ef777b177c7fe6a7532f317a18f136fde2224e443ab209e43f772d8074fdfea3b067c5381c65bb75025ee3f9dee77ce4dc03c743c2c27d44ef52d099e299389080c52600286711e9e1c734dcdebff96c9219ba0fc80d6ea186e6c29dc5c3191ebcddb75f9a6fa142abb71b0eb24ea3fb870d74ad181209cf52fb62d6a0b7c45d9f539812c01d85dee2e3de2131e9fc0e9e2df8e785877d96f7453353cd8e66536472753c38f34c924a11f9bcd3e1df5b24225211c57d2be13d95cd10a32f573bd85de9b70bda007117fd932d6670a44a0206cebe7808618526d6bc0230034e66ce9f7e8f7696a54be414b4caa9cd0855400c5b7787ab19599dffb8d491cab2af3858179c4829812ccfa8950148db8b0925224e70668ec89f91317e477701835166eeac74a894791d8eb76b1baaa550f11216c0fd4163f586a4e16ea766eeeda37ace01ec98c628056398c52e154fb4fa782f2af6aceeebd78d7f15b25ad97c9903deecb86333fab6be68bd8c9ff9facfe4214f0826fa40d3f55a50d102bcdcfa14680605bf36c2d142623c49239b8c22c31977245235b6d40d8c19937dd89007a42e79bc006ca2dbd5457f812fa1f32d9f1029f5a1a4d325d1518f606530e4b5d112c7fe4fd991d78467c24a0487c346a3258eb48dbc8290f47244f0dfd8152f2c2b6c12d1825bd4ef6c35da4a958ad6db192114ee30ab3d73821882eb27c746bd064c8d29a5f970c77faa3b11299f4bde099787744f327baf3422124943d9662f27c114be514e8c81cf618c21a009e2f82331c0ce3f8705e19f585abb4fd7adf1b9718854ad54a8bff4281e2dbeea99271874da9862988c3799ddd9031aaf478420767989fb32ad7577fa16523d27518e0a961ab41d2f1a80097e95d8c03919749c46f05967906ae8cc3ea5de2d4f25d8110ada472dc029c62964550f9ce1372c049f582e33fad78b81872c25d5e7c0521a3f7c7a4873e16b9def6fc5cb6b8bd7d918e64498de20f4de19f6d0c344e526175ab4d28c8c1f400c5b7b8cc871003b1d5cdcd40bb673a700a1edf8281ebc4a0e3721c6515c27adbc7da9925914ebab3eb9dae0cf2200cfbde8e446c38d7bb4295bcad28c370cbdd42eaac458f3ebe5cb7fa2ef98f984babcbfdf06f34b8f65091ee205e13701a1c2ea8a843f5c4b6efd809fcfabead21b10bde2d146aee526215a4fe1159a4d459d3e0016eaf546adab9cdf32bc4379606278bade85a725f7158a13d111c9446a19f6b0bce14bf1651b10bc0123a2a09faaf6919b112dddaa0a0ea3a4ecce723e76ff8831e4cd13d7424c4be81605104116740d58fb086a712e326db739292746d1985c1f9ae8899358e00d533a75efb5d7de1ad2742836322d49011ef90a17d991fcea02ac3274e6b2a9d8ac45e7227708a6ff1b1434586a11db76e8211ddca3020b2850fb31e09acc20b25a225d945840db8f189168aaed18b1ffa314189d1a899b0bde3931abf4178309c5e553cd375db69e22ba0131d8234633d63ff52a14b8ee82edebff56b20612aaa1828f1836d47cd48a889358a31c2ed3975a00706170f68e5c4fcf468d90ef43d57ea3f28b737ead30045fc1be2f085a00fca043219f011f86030f7f33ac46e1f9796a788f6b1c600c0cd7f74d21781ea6582c1001799f2d348f46f6a81f723560d7e920c1c8fcbda35d6dd4560fbcc1e3fe95963b4b591935b7506b55e309523f6ef0372aa53f6f6f39a8fced3033a3b0ff7bf32b9347d397c052d1114065d98e7a107ca39c1ebb71d52d79f101b2e9d8bfa85029b731263a28b39c9f0e9400d3dc6dc1c9328a022a74b704f2000bf3cb1c7232b14fed08b9c620894f038b79fa0dcd99081b21527561ef27b71531fbc148df29328c03a958ae4a00e803eac930ffcffab30209fff34b7d3f79102a1b45d43dee8dcff85bda9102cb809b22e2e10f32f6af247f3cd2b5db032c29d5334d06da44474e388dba32fef58f6f41b64ac8c0ec6cfcd4dc3e0f2673f92e5b3ff63c00f9d5296e18b42d1abd074dac590e7c6e16d339e714a8168fe9dc14582423af2f47b9078fea487c501377206684605bcb5724e6dc63c1e7822abb0855c33f180b5b20ebb2b40c27267d315c4c2bc75dd5cf4a2a5bceadea37ed78153883fb91a526e6f2a716b7139fe0b86b2a8900372f0172f3aaf9ee4a2019ff06e9f15dcfbba8dbb9dd9f3a28e9f54bfd43de7e0f8482a383ed5b608df6cbb6107cbdb223a442c91a27b79c4bebfab136990c609a006f94a4b4aa357bf3b4d70581cb1e022e7beb4fec5b5fb12d0c964ae273eb8ab4dbc0ca9e38768d621b831eb161ad789abe47c91993aafa4250b09fbac7fcb9f489d93a0be0cc4980ac561a170026532a309839237bfa1081eae9a778f6b16665de3237f00c8124fa1fd3253636290e6dbe6eeae74358d67aec6b012104749f53bd345b362b75ab3b549d088599bea97a0c7955ee2b01338bda7d05c5579e6822b583147e1d0958e70eee1b1d6559c7b4a21947d81d6d7f82d511133b9ce051588ce4a1a3e381ab5775ad0a93dbe6e14e6f4d040a4aacb7864a1468b61fde0d2aad5d57f75c6f9679afe9", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed00000000000000000000000000000000d50b7cdf578f4412a219947e71659c0f00000000000000000000000000000000585506e2fc08284102cd3ce553aa726a29012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f17b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e0000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } -} diff --git a/circuits/benchmarks/results_secure/report.md b/circuits/benchmarks/results_secure/report.md deleted file mode 100644 index c12e1bd71c..0000000000 --- a/circuits/benchmarks/results_secure/report.md +++ /dev/null @@ -1,141 +0,0 @@ -# Enclave ZK Circuit Benchmarks - -**Generated:** 2026-05-20 09:19:34 UTC - -**Git Branch:** `feat/1525` -**Git Commit:** `e0d477e9e98185779250f4092947741e84169b02` - -**Committee Size:** `H=3`, `N=3`, `T=1` - ---- - -## Protocol Summary - -### Circuit Benchmarks - -| Circuit | Constraints | Prove time (s) | Verify time (ms) | Proof size (KB) | -| -------------------- | ----------- | -------------- | ---------------- | --------------- | -| C0 | 287764 | 1.47 | 26.17 | 15.88 | -| C1 | 2432074 | 9.49 | 26.53 | 15.88 | -| C2a | 3879330 | 10.88 | 25.37 | 15.88 | -| C2b | 5739750 | 19.44 | 25.98 | 15.88 | -| C3a | 3764144 | 11.33 | 26.23 | 15.88 | -| C3b | 3764144 | 11.33 | 26.23 | 15.88 | -| C4a | 2564001 | 9.30 | 26.50 | 15.88 | -| C4b | 2564001 | 9.30 | 26.50 | 15.88 | -| C5 | 4395328 | 17.56 | 26.87 | 15.88 | -| user_data_encryption | 1678200 | 5.98 | 25.90 | 15.88 | -| C6 | 3001847 | 10.47 | 27.66 | 15.88 | -| C7 | 128310 | 0.54 | 26.29 | 15.88 | - -### Artifacts - -| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | -| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042688 | 176160 | 3218848 | -| Π_user | 15.88 KB | 0.12 KB | 2972893 | 193336 | 3166229 | -| Π_dec | 10.69 KB | 3.47 KB | 3553795 | 187260 | 3741055 | - -### Role / Phase / Activity - -| Role | Phase | Activity | Prove time | Proof size | Bandwidth | -| --------------- | ----- | -------------------------------- | ---------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation | 5158.13 s | 127.00 KB | 128.56 KB | -| Aggregator | P2 | combine folds + C5 | 17.56 s | 10.69 KB | 11.16 KB | -| User | P3 | per user input | 11.40 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | 10.47 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | per computation output (C7+fold) | 835.14 s | 10.69 KB | 14.16 KB | - -## Integration test (`test_trbfv_actor`) - -### End-to-end phase timings (wall clock) - -| Phase | Duration (s) | -| ------------------------------------------- | ------------ | -| Starting trbfv actor test | 0.00 | -| Setup completed | 3.27 | -| Committee Setup Completed | 20.28 | -| Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 5158.13 | -| E3Request -> PublicKeyAggregated | 5165.21 | -| Application CT Gen | 7.71 | -| Running FHE Application | 0.07 | -| Ciphertext published -> PlaintextAggregated | 835.14 | -| Entire Test | 6031.70 | - -### Thread pool (same process as integration test) - -| Setting | Value | -| ---------------------------- | ----- | -| Rayon threads | 13 | -| Max simultaneous Rayon tasks | 1 | -| Cores available | 14 | - -### CPU-bound operation timings (tracked in-process) - -| Name | Avg (s) | Runs | Total (s) | -| ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.61 | 3 | 1.84 | -| CalculateDecryptionShare | 2.12 | 3 | 6.36 | -| CalculateThresholdDecryption | 1.96 | 1 | 1.96 | -| GenEsiSss | 0.76 | 3 | 2.27 | -| GenPkShareAndSkSss | 1.24 | 3 | 3.73 | -| ZkDecryptedSharesAggregation | 18.98 | 1 | 18.98 | -| ZkDecryptionAggregation | 48.34 | 1 | 48.34 | -| ZkDkgAggregation | 20.01 | 1 | 20.01 | -| ZkDkgShareDecryption | 30.28 | 6 | 181.67 | -| ZkNodeDkgFold | 78.31 | 3 | 234.93 | -| ZkPkAggregation | 49.05 | 1 | 49.05 | -| ZkPkBfv | 3.85 | 3 | 11.55 | -| ZkPkGeneration | 66.06 | 3 | 198.17 | -| ZkShareComputation | 52.53 | 6 | 315.20 | -| ZkShareEncryption | 114.61 | 36 | 4125.90 | -| ZkThresholdShareDecryption | 251.23 | 3 | 753.69 | -| ZkVerifyShareDecryptionProofs | 0.09 | 3 | 0.28 | -| ZkVerifyShareProofs | 0.26 | 5 | 1.32 | - -Sum of tracked operation wall time: **5975.27 s** (often much larger than end-to-end wall clock -because work runs in parallel). - -## Raw circuit benchmark JSON (Nargo) - -Source files for the **Circuit Benchmarks** table. Persist this directory with -`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without -re-running the integration test. - -| File | -| ----------------------------------------------------- | -| `config_default.json` | -| `dkg_e_sm_share_computation_default.json` | -| `dkg_pk_default.json` | -| `dkg_share_decryption_default.json` | -| `dkg_share_encryption_default.json` | -| `dkg_sk_share_computation_default.json` | -| `threshold_decrypted_shares_aggregation_default.json` | -| `threshold_pk_aggregation_default.json` | -| `threshold_pk_generation_default.json` | -| `threshold_share_decryption_default.json` | -| `threshold_user_data_encryption_ct0_default.json` | -| `threshold_user_data_encryption_ct1_default.json` | - -## System Information - -### Hardware - -- **CPU:** Apple M4 Pro -- **CPU Cores:** 14 -- **RAM:** 48.00 GB -- **OS:** Darwin -- **Architecture:** arm64 - -### Software - -- **Nargo Version:** nargo version = 1.0.0-beta.16 noirc version = - 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: - 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) -- **Barretenberg Version:** 3.0.0-nightly.20260102 - -## Notes - -- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is - effectively 0. diff --git a/circuits/benchmarks/results_secure_agg/benchmark_run_meta.json b/circuits/benchmarks/results_secure_agg/benchmark_run_meta.json new file mode 100644 index 0000000000..efd38bac66 --- /dev/null +++ b/circuits/benchmarks/results_secure_agg/benchmark_run_meta.json @@ -0,0 +1,11 @@ +{ + "benchmark_mode": "secure", + "bfv_preset_subdir": "secure-8192", + "proof_aggregation": true, + "multithread_jobs": 13, + "verbose": true, + "nodes_spawned": 20, + "committee_size_n": 3, + "network_model": "in_process_bus", + "testmode_harness": true +} diff --git a/circuits/benchmarks/results_secure_agg/crisp_verify_gas.json b/circuits/benchmarks/results_secure_agg/crisp_verify_gas.json new file mode 100644 index 0000000000..c758c6a15c --- /dev/null +++ b/circuits/benchmarks/results_secure_agg/crisp_verify_gas.json @@ -0,0 +1,107 @@ +{ + "verify_gas": { + "dkg": 3125282, + "user": 2973001, + "dec": 3641070 + }, + "source": "folded_proof_export_plus_crisp_verify_test", + "artifact_sizes_bytes": { + "dkg": { + "proof": 10944, + "public_inputs": 480 + }, + "dec": { + "proof": 10944, + "public_inputs": 3552 + } + }, + "calldata_gas": { + "dkg": { + "proof": 169992, + "public_inputs": 6144, + "total": 176136 + }, + "dec": { + "proof": 170052, + "public_inputs": 17292, + "total": 187344 + } + }, + "integration_summary": { + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "secure", + "bfv_preset_subdir": "secure-8192", + "bfv_preset": "SecureThreshold8192", + "lambda": 60, + "proof_aggregation_enabled": true, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": true, + "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, + "operation_timings": [ + { "name": "CalculateDecryptionKey", "avg_seconds": 0.036223306, "runs": 3, "total_seconds": 0.108669918 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.156799111, "runs": 3, "total_seconds": 0.470397334 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.234711584, "runs": 1, "total_seconds": 0.234711584 }, + { "name": "GenEsiSss", "avg_seconds": 0.220404125, "runs": 3, "total_seconds": 0.661212376 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.202163278, "runs": 3, "total_seconds": 0.606489834 }, + { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 7.089881625, "runs": 3, "total_seconds": 21.269644876 }, + { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 57.963919402, "runs": 3, "total_seconds": 173.891758208 }, + { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 6.906346736, "runs": 3, "total_seconds": 20.719040208 }, + { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 50.328348527, "runs": 3, "total_seconds": 150.985045583 }, + { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 8.440406458, "runs": 3, "total_seconds": 25.321219375 }, + { "name": "NodeDkgFold/node_fold", "avg_seconds": 15.28584993, "runs": 3, "total_seconds": 45.857549792 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 2.887353333, "runs": 1, "total_seconds": 2.887353333 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 47.438696625, "runs": 1, "total_seconds": 47.438696625 }, + { "name": "ZkDkgAggregation", "avg_seconds": 19.61153325, "runs": 1, "total_seconds": 19.61153325 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 22.058676235, "runs": 6, "total_seconds": 132.352057415 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 146.017955028, "runs": 3, "total_seconds": 438.053865084 }, + { "name": "ZkPkAggregation", "avg_seconds": 24.53227325, "runs": 1, "total_seconds": 24.53227325 }, + { "name": "ZkPkBfv", "avg_seconds": 3.559760472, "runs": 3, "total_seconds": 10.679281417 }, + { "name": "ZkPkGeneration", "avg_seconds": 70.937173541, "runs": 3, "total_seconds": 212.811520625 }, + { "name": "ZkShareComputation", "avg_seconds": 36.93532184, "runs": 6, "total_seconds": 221.611931041 }, + { "name": "ZkShareEncryption", "avg_seconds": 116.868327526, "runs": 36, "total_seconds": 4207.259790957 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 106.505539597, "runs": 3, "total_seconds": 319.516618791 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.12560836, "runs": 3, "total_seconds": 0.376825082 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.320742583, "runs": 5, "total_seconds": 1.603712916 } + ], + "operation_timings_total_seconds": 6078.861198874, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, + { "label": "Setup completed", "seconds": 2.661124958, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 20.166480416, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.0012645, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 162.089313, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 604.932522792, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 605.453073833, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 0.353204792, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.000809292, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 50.493282, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 159.9251575, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 788.561521291, "metric": "wall_clock" } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000ddc537f44b53fe45e000000000000000000000000000000000000000000000005d2c364993f27cb360000000000000000000000000000000000000000000000036a97352d8068108600000000000000000000000000000000000000000000000000001db8386de0ea00000000000000000000000000000000000000000000000002bc93d2aa2c401600000000000000000000000000000000000000000000000c0d14f10c5fbb573a00000000000000000000000000000000000000000000000c707bc7be0aaa260c0000000000000000000000000000000000000000000000000002b67cbdce2a1e00000000000000000000000000000000000000000000000cd16482194b961ccf00000000000000000000000000000000000000000000000b86ad9a1e323001d700000000000000000000000000000000000000000000000de1eb22a6bb20778c00000000000000000000000000000000000000000000000000027eea9bef8094000000000000000000000000000000000000000000000002fc183d9b404e5b4f00000000000000000000000000000000000000000000000f8385a2178429603000000000000000000000000000000000000000000000000cb22ed841d6eb43670000000000000000000000000000000000000000000000000000cb1fcdefcffb1d74bb7961ace3f2f70c4e830de967288c03b173bd5158d3e5cd1d7fc55e865a03cd83a7ec11ca7d54aed446132af02f106c160b605361787022be5c8a7d0fd01c35e411edcdb143c708c052dfdedd4e54fc380814b419aa5341068557f1e98511bc88a7061ee755e52288824c391786d6adf9a09090ebdc23941b5572d247e416da2f3733c9efb99ba96eb277e14f3858272621b15735c4cf986eb6f9922ab32bb3beb4a44733ebabe5d94219e318b866bc934894e61df208758067275f6a7e1683d765e17405f0e76a58240bd4c830d911f26e464c227e57d2e92c9a1e85210ce7ac20d92d0db6dc8f9b9f63e554bb78536b2301522f2901d1fca0b0adee9402b670ab72f4bd26fa49e85c45951a984d4bee6c4bf2070544238ed41f1adaeb1ec052c80e75de7cb223007b5f194ed2f28d04cede81a64ab07783f001b76d942d16c4d740f877adb2dea5cb85ea18004f92f4f00e54481fefcd1115186ab43a0acfa1ca0a228aeaacf7ce2429f7a4e9581a243c4029a443ef72a3a321e1682d14a7f2f2757df78eff6768d1f653b2a2994b47864fc4f80e532b46354245b7a02a6e00ba0057129b07cf30279612ab0a64e41eaee79db02dd32ee1eae16f626201d6f39a4950369f70b76175c6dce4ba02eb128ebd52cb75682acc63f21b52792eb92c25245ec066ae29f6a9b69e7980e89a66b0e8ed0b8461e1a3cff3a41629169bb7a7faf7b158af57ac2a8814736fa48f50908a1cffc783e3dcea358a8dab05d4e8b1394a69227bf3fe6686bbd41b65ea8865f9616b853e55d01c263cf4601d3b429b3fbdbf6c3ccbc8bc7613a55080d4952205bea66a6487d50bdfdea2da1966d442fbb7b6d5de94a7f1f66b8f2be77d33671744de4db5ae847949c06e0509fb78c99816ad278049888bb597f7b08870142bb7cac68ad7bd9866571943eb26f4a3efeb2de8d56203502ad26bf6d582fafe6e893716505fca3da244cc36a329057929704d19799de43956ba1863b26da9a90cacdd883e5afe90272482e3831652a8b3992509c56f1c37be3f7da0d5ce3f5dff03f2f28f9f8971e78c0e257129790bad219e62911680c2b8262c491a19c65a69e2801de14920a8e49539f7782820f32def40017e1af00ec6d325e4c82b26d4dc738596294c15ecec975ad3b00ba9388f8956cfbafba1f6c76a2a9367c27738125e6f97cad3118711c432342e01a4efa9335931898b9d34cab011ec52bc1f426517facd578e65d3937685f8192b4abb55aaa96dfa5fc246f5bbe5bc9777f78e01e8c44a8fdb90646fd71d0d641738a84b0f2d2d822d017cbd5c5d9d7363eeb71b6098d280119b1ecf9b90f4610307dc478a39d30eb8998fad0001bd00ff2bed07374e5dd387830c146323519e08f35ab390d4282810ac470f17b1ca6326ea2421d6119c6742dcaacd76c30286282dba21e7d7389bde163c696622d88838635b4a546d17f1600c18b748c9f72c2553375c44b53f71a52bca0c87762df6552408a5f4500965614a5b03d4fede551658351460482c296ccb4154aab50e4ccca5cdc55fa75423c3d84c64f70518b208aa01ded1676e0474d468ad377ca4d2b89ab3cb3d3963de0a6b13b1debce552033885824e78ea0027af972aa760ccfc5e7fe7755a65a6c6e4d532a33aa47c39099e1828b109c1ac9114251607526ecbbaafb83e8fe2efb0c5388ce62ea3711b2b28c132f05e1a371ceaa48cbb778b4db0530d863efe66dc5a00840ee3bdc0f71cda948b42a5a7059b54dfbec5be13a982ecd3d08e07db4e9b740afe7b136f6129ebc34c7ec5021908b52d65363cd12854f10066c1a0e09a9733d4a304ed41801ee2ccf5b72db94d42ead97f746f5b92eabd9115d8d3c47c7198f0c59f61c82c2e6cd40132c27b3a7014a3d0404857ace260b2cef00871951e08d2fe4af80cdc20b67b6dec13d790f840cd45bc09fb36f7e9edb1576aac1fed6309d69d9b4bdc23a372afc89b2b8c7f8f0b4d5a8a9964d817ec367098dc3baf84a14233a8b24c160d1e3cc9588f8c29ee86bf38c8467751c54dc206ecd20dbc041b9edb0efcf721eb824ff79a781cfc29baa77eadc827e1faa60d2ca595aaaf41b05f47403df5054d2e48bd550fc31e258c9aea79d587deff0e66ab7e24df5ef60a675dc0d5d61b9ff4f004a2ccd4320ba46ab42593fa9ff56741d268418ad57e8cc69060e112173229ebb2deb9626294c2c341417b9274d7624bf0f7bfad9d66b10d28b8a03116f1913cce922e6dba33eb29a98210fb69f529a830d6040c5f0117608ca3ceeb2301f5fa825d225c3bbc88b05c33ea0e41eb014c66441d5a9947fc4680d35e421dee79f2143d0497c9179af370b754e0224e003d05fecab656f64a823fefe44313b30bf0007ad379d1ca866ab25f493e8e18900fb389ac5415f67156120bd77c1d83e3909193ba0d2b16ad19811ceb4ad3d742b50246378e6fb6e45468e9008a1775f8bde9dc6a8231c8f474efdd6f11f695e86315777cc234f2c04bf87252c12fc8850ee7143a98fb139e12b5cddcb9215993cd57f93020e31082160c1540fa0ee8fa70c9c36ae886a7e4a52840ac356a9c28b4a0cc432f0a57d049d9c60ea52f9afcaa6d4f8d79c21ae5b04777811f9960f0fffd52580320d0fa196009634e0b20b84cf5dda38ead8c9dfd830c5aeba4fc6a0d4ef749993b144954888993560f5144b9c881f632066f6c63c95f4066fafb419bd9601c028ca2546fd6c3efda0b99e191422cdca5d0e71bbad1c8f5e358b973d75042b1dbe0d437393ada02fe22b41dfbcfc9155f55bcc64ecf82d0a67d810009043901d592b0a810c89a910b1ea0e956bf5eed9782a9ee48cc3a97b989f1ccfd38e3a2c24cbed0109ef89d151c96ad5afa7c6d2e7219744a1338debcb9f41485d602f9c2cf30efb8f22adf390291fcf0d2a9ec8464d8180854056c10dadbb17a051f9c2fa1389962ae877246190150bc8fdf39bb570e596c3ef14377483c735c8325a32b2709ca1e939c9d3304855cb7c5d17fad910f9842c598b174fa9c275096076e45af017ffe4d0f9b41129d3cfe4ec19dc4f72a61ce91e290353e9d411dab365e3cfc43583fb9c8be7c2e0f7417e6953d9c8116c57bbde9964dc413f454ba813aa314c4c9dce76b00c9136add93dccb1318d338c95faf456f6ce48ddfea69c406741a3e59dbc207cc9a190806c021ece63a687d90e4ff7fc6172c53f1b23827409310b5a44f62fa60292055a94c5a8d3471dcb94e967000cb2decbcfa85aa6bfbe4743e08a1ee230ace1efac82370ba2f09fa77016274bbdd487475e100862dc8fbb80382bc69de285c0bd0047ba0aa8ec022bc23168d5afa5b1d3a1ad7bd4b9c2ebd431d3bbce150a70f29dd3aada8fba9f7cd5d5db945f024a9dea0dc51a27752f0be20b4483ed1f5134d34dfc8dda93da80b5cb0135d3e4f1b3acd5895afe9f691dfe5a41109caf52391b88832ea4c3d2b8627b03beda0692826fb896c237e415c9058a78be861841bb561ac86c2c8800167ddcec4067160125bbb52a30f0a1c7a8cbc2cf4e1dcc107d74dcf175389be3287894d9a8ca93a7f13f4b482f4d96b5a755388c175b67f077bf625750b2cc8492e3829624f6acaf7206c352edcb0860416f30c5900359518496a52f58dcffa59213f02a3968e939b821acf25c1d54ac8e27b5037799a971a45440c2bf0c8294f291765bb3f195dafc6cd825641f439c909de60f22df7d90570f8581fd144d8e336e732cf807c575696fc5974beb6d327d0f4e50fdece5c20104dda0e51dcec48d30f4ffe4765a5fa9f4a2bdcc2ba09ad027c2ebe4188cf02c493f0beec1b3aec852dff73de5ba65fd4704359f4e7954158bf624d937a0b09c929bc6fd95c537456c59599363f86fdfdf5b7d2b58ef05fb1b62c4f93945e1e9b7f217eab940cbd9d3dc027a2614a0e0ac55d6a5f4fc517c77a610772a6d5067e09832583efad6905bdbec52a599aa83ba05aa495f7cff70fe7fef1e8f9e322b71ffe815a45f39d91a5606a09f8725c76214aab8c32bc3a2ca3d1c4f6f342210f1e70ba357d8e93e48c3fb547aba790c9548057837506e9d4023e026050ca154cc3aaf0c7d60316c7f56a6ba004273b5c1a08881c83cd592b770e063f5db6194b4543e50e6b2d4fd1828703ebcd61f0e0e8238c7c2907f640ad2245e6f8df296178c5b6c882031dd6f0b4769251c1cc7243b4137447d868802cef801b2b7c214299d10918e2da7ef9a09e2bd9e11432798d3054d50e494c0da81d33d89a2607f681bd3f241f731f09477538456c7b3a0157ce568d7d4c828a4c24dce4e2b10cea115a9802c9cfbc41c26092502ace91f9937f175d77e3f165ee57b2ae6148293043f2a0c21b1110c61e1d48ebd32277db4f3f8fa1a44148cdcf7ae17d901310ba46c7249cb240b5c8e89ccbc193ea405bd845baf1616b538abd45acd32e250b0eb91ba32c5f67e81b79cc12e4b202a3a514ddf1c33f55d76a78dd2a6f431216cd9459be0440312a230538e2c08e9eb699323c3ea330fca18c022801cc6bcf1be0daec57bbbd18d2b11084198ea0cbe1d5b1f16f4c9c2f912d01e6f8b386610a28f67f554cd1490c99771a65f50623d2fd504f9c4ed37c92011ff0b00fd73f2e96733327ef59d02451b02b31243d6de810ab7d4bbe38cc5c4132712f9e649b292aeed91c210ad7f26e8b460f9a2cbd737062f3479f6790b0d85e6b128901da2c988129a0ed53916b48753bbce885de1a2f74afef8cf38b2dd4d8aad9fd5a7e13f32b602a56d2fe14c56a40cdb217be8516b43d718ae6218a02c8489fbe23562cfec06264e235fb768619a1c68daee1f5343fa2fbc9dd496b59aca837eafeca1c526e5ad567e4a3c658bd6de68af23a0a436df9f7f3f29f0ff683cc517a04580fa8bd2d49661956354885fb019f0cd95b7b7db7d8de723a7cf201babafb415f06da7a7d803e8584a58a41c24ae54f7ea86798affd2ec39e5971f63887d3bc631b8ebd9aebb270eebe21f676d194077a0492a039335bbc974e1a7e8e5e3b187b005cfce57954aa83f4f1373b857dd0e9108f15b8a3319cef2baf99a947447a6e0970c4556b9cf6a5f7c9b1c4a16e1327cfbb978db10ea891a2ef12d50a37cb040ff5378b30441161af327ce465ca56b4093e3eda29ec439c89dc32425bd33a6b0259d8d1d4319bab6eda9a614f0f78eee496b69ab9fdd9aedc3391378993c459027d87ba74779c68221b5b1e24712803e9f034793753d49d661fcc34af88b67a2eb483c76fa9350617c43cb676482acc4e4516cabb406264d4e41fed5f4d800b197e44dd76820dc1d207e1eadb5fbf61ac15b6dd1c7f34dba0210c84606971fb2094e779db97d0fe0250b61846c330025c5c951e7ea310f0482708886d6288c12527abb45cb435c1579afd6dea2799b69e5298f3e6325f2926583697b5e2b5e21515e902fd878d2706b6377e4a86dd3e75e9f1c6d5358b69898d7579e05df9ea09955053c4b5d8f47e09260b20094fc317222707a15fcad33d54ebb0a286f20522163de47ce56a75b960afcf3ad78fe489bce9499c2aaba40725da1d0d3753eb23e9215f54dc85bdd4633ee04cc3feac16e1961804fbc82b69144f0d02cdc09b08635aec4f87f85d0f8c2c235c257389f68de57c17570ea93bbdeb89aa489c7e2d06f6ddde1845c36d494c75adaeaf4e7f71ab4c3ddbff29c3a02b1d2df0bdff2fd17b65e8f3ef08cc0bfc3298b30a9ecbfa8748e5dfa71a6c479b7c5a2c68e60b8b5d197e76d358697acd62ca4d0f0b4ab5c7a1a6947508e0f1c5f9af1a4d2402ac2c8f342cff48a42c6cba919a995820f017a9215ad63a7cf6b85aea4bb44a2ddd40c367f7b0d4db0755f1f0513e8524e2fbcc4ea1cc65eb312ac3a0ff85f4287e803ad96215b8d15abb6a304a8c0f13bfb0d673d9b9a188e8bd6b56de3ee426c3ccbd67dd0b3996a9719948469dad9f074a13094f68e012cb4f6dcc18a9d92c504aaa12f96b40e0dcc1822332351be7c39d99a32b099abd4bef860b50e58a03bdb1f0fdd97afd4b44426fbbd310c7162282cb6cc5681a5d34c8792e7671840661eb520c57338c07b2b400bff64f9098a66d2aa940eeb9dd8d080e274620b022e6a97b9dea474a50239371cc56bed9a42501ee62a633aa6808daf395da3c8816947be7efd0e3c76672fd721740f8984d4c0529770f90560ba8a139c4f7ac6523acf595db0a22ad14e9552813447cb062fc03588da0bec34a24d282a7de1889063cd32f9dba2749bed618a058e3be4c300735d339e3f39afb597c470c287c3620c845653d8bda6ff9403c4e297a697a09dec0e9eac2cc5f590f152ddf12b0bd1f0425e8814a418457d8ced41d0d54e10057d8fe626ed42f6a69a7981809fb310a321c501eaf6f32dd062bdc21a63ad2b9a509a0d7d7362972449fba64911a3f0c850b147619d6f54f6437612fa2c18d1bec1016c640fe49367c05a1096894e4033f004ac462a4c4fc2aacf97f4303834e99878ccd540822891a8cf1baccc0042989e91fa78091f21264783ee35463fda706bd6bf6c3741cc7191892050c36c611395fae2e9bcdbb8d8d4a820a1391d5b4395cffb38f1a23d3ad3389c4e13e601842eec0dc4f455637e9912489865c89a373fa4d6e0cdd95b9a36043032523cf197a2f7a626193b18e466a3f5333ab0a576b7a6c572c8b67352f13d570809d6d0ae6457f3be1367321458a0e4cc1453d02460bc831f9c268488ad05f1433ccd0271ecde0885fc671c03a6d62646ea177e0bd6d7a3c579ad57ae0cafa74ccca141c15b844d1c1ab9deffc01df3f7fa8396ac7fa4c7576f689f22949b2915bd6c9161b73eeade29e2603da9d7d86e6cb95baa6c8bd0381b284378b2cf964fcbeee2d05ae518781466b3302f7561b144d6ac87de4bc42056911ca7f594555e4a5bd2c228205de015357c2903f7d9172e9789359250088e4797c458c6f439f57ec881fbbca82dc4ec32547233fd8b9d0f464e8c9cd656e151e6e7f573cce989e8335259537582f5e8096dc5b6c1a2d5f1941b9d01761ab23811b7e86df9d9a9ff7de2304576421d982b42b61ddc6148e03a73deee2e734616c495af2eb08c76cce640795dff87432b303fd8552a8e0bfc097667dde8f1434a2a4458e7e3e63b2d2e31fcc077096bd6aa45ffe9c70ae160cb4d5e2d28854bd889ba3155ce0ef9d087002539dc1ca5eaa4c94ee4f6b8f8199a1ddc619a061c261bda03b79c8c1ff158124ddd94fbe30c2ba65f28ecd47d77724340ec1fb1143c150a014e2241b78c8801364f1b969518cd9d8a46e0a4b2526680351086bfe08d3a0611f5402fc8ff7a2253c9dc9121f43c741d9059fdd5a5bfc68b8367576fcefeb2103d8f2057614a42e38726adf28f52dbc15715e283dd4d3e315ebea3d87cb5681e648783c45631b17489def600de32fe48770d6f8b47af009996c343952dc446322afe142c7a0ae2fa2ae3dd68945a1e5b1cb742a36aad5bf6ece555e28863ffcbbddd985b201132a1a3a08ac58724b1cd5330dd46b18833b3349816a5dd935d98cf89215f41bd51294aaff14004a783e89eec6acd648afb645793dfcd5e4ab7a6406a7b06c85cf00533eaf77c6ddbff3d25aa4839cc5d6aa0f14a5925c0a32d9a445768524164e2da427ccf8e54b7a9bf52299e01b91c868cf5b4044b45ef5ca65da8d3ee799fa20549181f77be66bc9817005e53bd1e531e6dee9dd0f20ca8c3fca42ae8908872fb3427f975bf8bdbbc304d78748317f4b67bd3dc7de4fe9b1b157edff159e561ebb0f00f5b33cb3909a8a497869f51887c3db6a9b772f81fe2884ede9caa523046986bd5d116527e1ea70b1f80828a499e31debc9b76844336025ad1b21b3d2015c6b1a54f0545b2cb529e8afeda16aa48ce0d0ece560f74ef2f581c66a081600066eba0b1bf195e16462af77610d6a151195830bd0d055bca6d1849483129a18ea91d797dfc1e0f6d07a3436d18c0c5f334a5c4aadf2e5056d19d0159c715e2cd650319d32667e302d8a9fef3af6a9545e0849f21a07796e59057adc42627a0a5f8c5ea318a090a49792860132f0fd5d4919ee50f200416a5a2af10acc58971f811402abf9047c9fccbabc6f83492467fb15148eefbf12e03cf4d5dc73c8b11cdc329719b4f8c52eaaf5815a0e933a84288d3b82bd6d386fbc4aeb2bc54ae70d67171c67f54db3f36baefa981e124737f1ba0695c790535ad233b152fa5892175ef3e3f23f536a8d0a6652c20e2e82576798cdbfcd649236dfc11538f345b405005ac1a5ccf6b49759d8798973e0df4e77e0c89fd098e8419abfe2f5896067246380154e1c9194733a0642df08c2e1298030c2cda913afccfd1f25e5bc92921e6e5271b9d827c85fe64ec46d1d6b1b7c7652c3fb8aea513e179568c8b9fae1064208d306916576137f0074229737030a22f693a6e1e27ba3a2799240cf04f21a4906b79794c495cc5a0c3172f64240935363b2a03b8a498403055b57fdcbe60e976d7a65534759f378a8367c72ca4e53cba16fb2fd4c269be99f9c0b95766e199b0cd0ea8195a74b08a789ef979eaff7f5b1f2d2304b25fe75959e4535e2842c510fd683ce6a57e9f88241a6f5e1c83b344d1e48e8eef6207f4c695a7160e42d9da2b737bfac00c86673a90cf1af4207b91965c1452df1cdbce7fcec877fe50ffb6145770c0185228a56decdab132677f095f41faa588913a7dbf6d99b9ebc195574350eb6ffec25f1454cb04f10bac2497afa7c07b1fe972a0d9a5130df5200d03cd4e2d8dd918eba724e04844ae0ef024e4d0816c80701c32d846e69b3d1099ab8d3f7a5bdc46fd4ebcfcf083126cd4f29058da7f6527510a56b2728c5a305a5890ea3956c25e5e70be606eed43f110c13e4e5c62a295060104eed434c290ccd370073feb94b0cb270833420422239240910eb8bf68d9e64695bae4ae47b0faa67b705e5dc498a3de1fea3e10d1f180ff73a45e47adec3bbfe0e6f74fc1508adca94c2940a080ed3201011cc8600a4587a4d7b5c118248d435316f6892b60e1c4cc049fed472eb006cabe94d3227a597107ee6ae46ac2b0495da4e2312bd2c2665f1e8f3b3af04bd11268ea793221028ac9a53af4de4469bcb38e6b7805a1bd4534282283baf404766e3aabc57e27eaafe6e3d526ff63ae74ad59ccaba350bdbc329066524225e4c83df6d4976b67c75a0fe0bf26ecb8917a001ff03e5182a561b20265f192fcc1e8913e9b604dd3a8c50dc6de4dcb715aa8c5ed6b1dee520849e74cf1e25c665e7ac8623d5786b551d0e83fdecb7a2e048918f795f25d9245e7c2b4b08639891fa08744c96c8ae610c5e00bbd4c00fb8c78cd5a56a6d9d1c3b91a8aa49d2bc9aa7487ee2675248afd559f2bf2e53241be700c20330aa98139849bbd25fa918c0d00ee715387f388e01ddafa0d9f32f535026c2a1d5e5ca1c7786f70297f692765b5c2060e003779ec678f25b0a8ad3f09003868e31d4ca1490751969755664bf9099ba09af48676c203ef6d893819fe75f9f080a9010cb0641d4108e4a034e1066aea62380082e9462d5dbd9763295152115d3d906e0f81405b67c03040f1fdba9e8a22291cee66a213da00a1a82939ca5585889443c582467dc98c1ec7db2ad0cc5675d6e57fdd11b0d261f95c9c493127173e9abb79820d59d5bc621de7d92f73eb795acc475365f9ec3e00f62de73e8aa17b0d82a2b0704b5f64f67f0a5bdf5b78ac8e7b56323104b97abfa18b8f618b6ff8ac515d30dc7e24688b5bc661dece854e144c2e6844fa0f73151bec43c707b8a904c1ba401150cf476ac133608609a2deeee4be71914f8e2dfa87d9eddab673fcefb8dcc149e3bd4398c3e21fe4f9c94a3a95b0af114e67e4d3256f6ca5769f8d030057a03c8392bee6c04094e843903fdeba4cb6b1200dbbf22b1c057b17d1fc74ebf0e2b948c48cfba494b57cf41d63076d21d63461940d91ad5c7e8b0b9119bd9f070029c395ffb2aa0e6a42c50428ab7b9e4601f855e3b2f5a517b47aaf4530d4c3703d108a6c9ef64f6333c26a8743f82a9de11622f91ba4df5453d0d8232a12f1807cdfe00795b0fce05816ae6000db48ff5a3b377cfc808ae6a34a1b751a2396709e981d204e448bf2944cf4eec8b08c07fe3e7d36d29378fe0aa213d1846767c0cbeda7d1d59676a17858927d239317a93d6abcbf2d85c35bf6887d987f8b1dc1ba48554530c6c891ae05d490a57578b9dd98d2f5ab56ef27e49d60fac56abd727f7d7d9b9edefd4b80adc1d26d96a02055030ae5b8b56f5370ac99312cb9490248fde5d277d0de526e0e8e4502190cd6bb86addadf33ce872838dff1bf67e8322c55d8f29137c13c8028fd4712a8c59ef5357e978b01bfd5de3caa9372195d51392e91f4e30a060d554050d0f5f69017511a188c84eed0eb705862f714268381e81321f0c2fd174a4c84063444dfe47ea087231e50acfe22137c25ce4d6bda02598f4d95b3b644a74e80a2dd2a9cbdfdc60db1cb3d9e81699915e8986c24a992b9c3c317cc4e2323de8a35c71a5b48b39196ae83ebaadc7b89c2614c187c4122b0d2003927f576ddf71f89698fcc08c9164a02dec95f7850e592dcc22c0d5532dafff7ed0586e92406009bf7252044ae37de2de248a0920419ccc9306243bd20d02984910cc33998f22c46a75e769312f46335cbe0839b44dc97f3a8dbe4c9b1eef663adf180e97f4dc8193376f7638a96512f0162bdf052c6f70b68712a9c52394216753b6e40d27875f59202d6169a3be15181c6820fdc55f5520a21feb402ad28a19292df4f84673c3e470e5f4bace0c17074649c80d78a2fceab1837af4286e0556a9b68e99edfda0f1b4d816edb9a17133b306fbd713cc57e73d7fbe4626e8a5db2e9ecefb436349c7c60683cfd01d8c292be9e7be832a93466e72662b06345508f78a1c229cad1d62974be787f3b43446e56c0ec14c7c1916909c59681f510273651cbbfd259282295fe30800d0abc96432b550df4b2cba15971768190a089a294e6395f7db1923b045ba49a9288344abde983e246667136886d1d6451c2e590f932f74a6919e95b43a356b38233212e30eb1ffa92a972031fabaa5992ff19f5e7adcfa424b76e1794930378c2ec7d417b0ba06c0995497b4bfb5193909852443a975b6f2f1eb35f76414bed66cdbb4d78127d777085949554cc066be0a389061a2612de4e3aff95ab471fca7edd7c325e46b6392741d0a8a6384393e1d88ee0ab0217ca4cdd7a540ebc592450a7ccefb601b787b8bb37f87acce0dcf05aa8dab9248671e0c4ae48e68be16c28e9fa7892392c327d920e7f228b1818c0fb6ce357e2a6f432a73b1625270de7598cfad78fcd4ed8cd61e0caeaea73e90025ad2263a0491cad774433a6a2a5603552d2432c3562df1960448cfc5c77e9600a342e5a0fb59d2d3c59a5cad80e7e89a6cde76404a5192b764aa2a5440ca4f2f53b36d1f973eb8a42f76cb8b85a96edbf9ede2a7e45e873caee089870a64d30154122507aef82763f71c119738f8e630ba183c3dad4bd7dcb22e154915ebed2d3049ad556433a3d477113a0d1ec2183cb40ecf9c7b2205a9d2313b9d284f881eaa5a0df0c779a62ec623fa625564c908819871d0eda6ebb87dae27cce9b217108c253412257762231fa826d1ca59d856d2422a3b70ad8db8f835e43facaa7517f412592fd56b55c738e15626155265f5e08a30f9e2380ff939680fd31d5c9818147d43dedd9a75c130e01343ebfcef42f34d9d58a395666159665edf5517a715e11370386245aaf38e979a6de8d004b021644af89e3a8af6a9895e15ffa5a62a85a2ce71fb035b791c43e9fc861c3fc0378cc445354c2aabe5fdc8fd6abc7e2dde7e824d41360f1c664770896d164a66d81f1c2099b852ac1b6231abccfa271554713725102613e592527272ee3de12a518a648515ab7bcbec838cba30bac22645dca6372e6823a08deebe71d5ea98807e9b3ceab9bcb3e98d3d21fb7bc97314374f8736eef149f81d7e60d6d08c3a748759a1e1b25f0d7aa34af578bd8d1909e5ee4dc730b341d2fa5b0f1e1c62d5491d963ef95bf6b074f471cd67bb3be21f25316306a63927aee248ea0e760d9e8991b53ae4344fefc91c6931964a25e70b13c0df04d755b1031a1bb9cc7c0cb893f033119f60ed7916b09a4744a7c4932f9d724b44a7274973743bd5ce061cfe78aaa07e08d9f7a69e8acd260ecd091a1ec7bd6bc2c86edeedea3839063635ccfc602b537edf32df8a6812eca9599b082cf34280289889631b9472c574029ebf387bdb6cbbcbc867e90519cf30e47224167b9eb6869fe71cd56741bfa44aa926f0373e0e53ffbc68d27fe5aaa2f7a7531407a65fe05dcc42adbbc2e15a6afd7a3c78e2f101fb8bc68725bd41f079ee5e2a5d465e57653e462f4cc3ec94e2a007b90caf34b0677c9e56aba559648b740822261f54ac4a38a21b66b734917e2db39284bad9731e58dbb666de753ab11f8121b935c281a326d7fe09dc225c0b5c0b5b0c315ba36d6f9671d83f86c238edc12e2a8ec4ef750cb0d3d098e85595ba077f002771dcab930abbce17624af0fc9803bb78964b8f69e83af710afe5d6cc8f47845633e779372fd4c378ae0a07544714bd1fab35ef607011c6d0928c03ac61f8bb16ef1689c63e520dd1e0061aa08502f49c527327715326a820d485cd07887a0d6e8adcaa8c1c4fb19ca753f2b4911717e8bf9d6de58b0ebfff6e1cb1c119116acfe1139ae905d532195621e8c7811b27a3237feb01af93b31c594c294f44db4b13d52caf64b91b28c85bdf10e3972e3fd306b16506999526687bffcc8b26ebae98d5458b10a56eadeb0359dafc771d5c821d5148fa5058ffd8ad8879ab993239dfa206269faa645b647f130ae331016c801d4200a4177566cc3d3838c45109a407b55de78941ffae8fe6177b1d1210644651ae0a77c1c7ab833a81a97e2b30766308b80045d91a5d4437f6c1b0bf205f4a40802c211764dac2ff9932cb07239480b1c0db1f67befdc739d191fcc40edb870842d4f014faae3b25722c93da7b5356ef9f59d394706fd9b4848e6cdd1f6d981d02c1e5830ae59799d597b732447560c47d82bd2a41ce7c3f119b064d10edee9a05fa19bba76589ab0deaf8706b80a7286618a2c66fe78004183c0fa91651a96260d0d914f3f8f3c43c77163254d0a09b3fc95cc11f92350ad2e33f6c169d8029d882d113f3fa27bcf5d4edadc971370be191b313598bf2649c391016007fb5174ccd52d7012d39d1d861a731cc45b296ba1f6845c912e4211782a09a2e82d38f190f8ffffab2e164ceb7a39337411ba9f57f9ea55d6f215a47a897bc2501d65e640c578dea884cd14c5c854f530741f7e45673d9391a3e40ef11f5080cd5e73f44a0a318bc94065bb85f4bc8bc8220b5dada187ffcac245a5fe5fcd61c0c2688914c38700447fc88588de588e942e4b73a32966af7b994d407f3bf2f1dc0d164711f06e29c2cde0f865e99b56e28f1c6d9b3a8d5976edc08efee5cc029959b87f456b940aff559971f91334860bbd138282f42369a3b9b1a298298201ca74935b9233330ad150343dae999e788a0914e33cd5dbc2125239ced1e0f922e57d857cdc852fbea618373df51bd7eb85d605c0765a81d70e07ff4870fb4512fa199f4529c894809df36783c415b4102b3671c9bfec49b039f6ec3af71a0420c9d9d7da74284cf82394e1fc3cda21c5a6f0b769bd841fcd4ec5cfdd94d18cd1ca649c949279e93aa7985fb1752a6c0535cdcd5290523cfb3b1e1675b9098341052cf410383a5991c77cb7c4c329dd77da3da6df0a086c596134a966dd14f71234e7b68f13b28313ec6e69d74f41532308f6582b6dd4090db9eef2e26b177391d074f90db2eea4083f25148f5585168aa5941b6086e78d1c5d7425329465ac9072d1d51448700c369ffafe578e56577af31dab3eb3581b151fb0c81435e9e451328ba5869ffc8b023bad9485a18be42d9b36f455a2fc8929b98b036257f6e3205b75ffb76fd5e259e348d4ba64aa2862c132fa015c12fe25fc56cbf76b370d51ba172bd0d5f4ac5f2fc2c1e9e33e5281ea25504a3121a5a9055fc28f59044702a750776c47fc301e959e462d940aa019d5d3e6993572d41848668f6687f3c30076bedcdf79e500dd3d6e39fb8dfb771e0c1f61d11a83ffd13deac03879d282d1751f5771ab8236b6303e5ca5ae50e12eaf2c64e02ad5aabe15ff5bbf2f7d20017165e21a1b3c39e90633fd4e52ffd25dabb3c2359a6bcae94177d3b069fb6632c0ec01b6dbd3c695bdce702564f350412c6c7826d036befcc5b1fb5f39735022780f41ab74fca8eb93edac43ce9fcee8979fd9c3fe80894878a0a75c89f962a2583188a91999edb980c6dc1c0b1cddbbfb0dc2f54c1831b97e1f2d58687fe2b0dfd7ed13545140103e66d9ef7bf01ab4c8d3b184295992a34f43683c66f373e29154b5ebcc4f8affbb07b63eae67d86c7e2a58f4c64b6c3e0d5f6e2e2f962860eb40f1c56f6b2cebe7e9af8b9b3ca86c23c3a7bd479a1df867a2dae4b41088c0e02a0e0688ee555038909a12383e469c8b5c8794a23e11b473776ff83ca8ec029961ad53a2b243dbcf6093ec89a9619606791535381bb773aa59d76a0bc0275", + "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e974750780303f2ad158d360e938540cb9fc2ecbc5168b0078e536f2a2c847cc131d440ec0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000c6e378e01efa1cb216ab0ced340b44360000000000000000000000000000000097621038a18ea02190913636079ece971a28590517ffbdf7ff33ba52157bce68c37c073712ba7f38d05290ebf864b98223de00e9e865ef8e388eeef184b6f39f6bc05eb3d0c54021d2345bd1729316ef17122a4b371cc1b35e37beb851894eee43c8b48d01f6e641ad9ba6e94da997fb154d92131d18a3d5e38ef60661e890f7ed9c3c630efffcd8e51ee7278f13333e167714b66b4e3e97a46578eceefc9955b033a18e6f8ec8add6f96a917a74414421b654b589d9ba8107e6007b5a8b71a20fb1deff0cf22904da6dd4303e7342972486fb2c422fc657a57fb1fa3501d8a94e68d15348674a20401c431592ca2aaf2dff57e20c39913382bfeaafbe4d2daf8171ebe9a277a845723db5482f9128cd" + }, + "decryption_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000616da7fc6a6d8cf29000000000000000000000000000000000000000000000009587bbfcd4668d960000000000000000000000000000000000000000000000006121d7f725ab890ec000000000000000000000000000000000000000000000000000254c3d3438de000000000000000000000000000000000000000000000000da6daa0c6a5d1b5ee00000000000000000000000000000000000000000000000704ad42ddbc8965a70000000000000000000000000000000000000000000000044dae6888c0469bbc000000000000000000000000000000000000000000000000000038249694578500000000000000000000000000000000000000000000000cbe544e1f83d0969a00000000000000000000000000000000000000000000000f022e5672b0d807a7000000000000000000000000000000000000000000000007ea76b9db2039b97e0000000000000000000000000000000000000000000000000002b08a8ad0db8d00000000000000000000000000000000000000000000000d86835de0e0ee2dd80000000000000000000000000000000000000000000000069601171ff6c2bd9200000000000000000000000000000000000000000000000dc64e0a8399e2eb41000000000000000000000000000000000000000000000000000122eb6e0ea81d1d0f557691796b92e9da20b93e97a0863ab1fe2ee78478842a0c49f45fc99fad1db541b4e5adbd88c9ddba68378f955a0fb8fee6c21ddfc8e2fd73d6ff009a283005d6958a7b2064dcf37a906df83f6046a1c41199c4fcc7b1de51bc48831aa306e4801d58d108d333a100de61c3a7c902d11c47b1f3c7d3a0b25a6ad72132132f09bb7f0634f4b826b2ba79aed873c3f63f0bb72625a7d02b4ccc92408ae597051f526e4e6e94fee05e2ae2d8e4b12904bda66ebd9c9d51e9961cfced09cbe11d880ce4fb8dbfbd641d0c0636c70c7c15bf3e232af15eba8b04b2cd763212bf230710d91859e6bf1de118a016a84a1174041993b37a39e1bc004c1b8da7c3b82b019a2526f1f013dbb5954649960459a4c506bf44865e1d6806947274b51dbd0045c746ab31b273af3beb543fb20fc9cfff80cbe7db0a0b511d2f9b14502ff12f5766c56461c016ee71291019c84e22c333cd16bf76310569b0871fdfc9d31b1da62e4933273fec94c2a3cc680ba04e9b6b233a0d13147367ded65fcd59d53406e9772bf6e4ac857b26dc812bf34a1db26c1b64a233b0260626c2469164115910b03889019586c0037c32ea2286fbe1bb31259f82545e8627c3b9e841226d212afe130cf4a9b76961d989066bbbeb66a469dc0b056c285083d68c76bc2ee0f10f7a0f8441de08dad090e4f7d846c46c311aa23fd6f18f312c62c8b007bd61472a2fa97e9e5e5abbf3e1351c8275791fb7d97b289da247a09935c9d149beb26a1d562f06793f41cf7b646a4e457adf890d6f0bbfd0ac668e53ac69d0eea63b8329280e22aad07e3aed7cb334e20e466b04dcd0ce6085feb9a6e975f08da975cd1d816b4b170d94dabe92ba65bc039266a2814b2a91ff6d9cd59dc8488720894a04abd5ba314a630e81139ae7217116133f998269c10294813aa4775e0b16da5a0d23169f49db334305c3cbad59b33c9c08ca8fa4c5ddc084767a3a1005877fad196bbf853f6103f56544f009d9ae15132acb6b48466e73ae67d8c56770d2b4131997c4345fb6896ba5378db26aa654c77a7115a54cbabcf7acc4d40bfd53de0211274d7054ac3355db0cc69e8800a1e98400c4f70a8c9de3d8dbe179b2d44da025d39a26672fc01702344e9a0658c0b20ebe5a51f8508146f73ff6e1cdf6c2e81cfe68d354f0b72533115a2703b6cc5f3f8dbf779cacfacd8680218727155f0b04ea4cbb18eeb93963d0f419b0b5fa441d4a833c7ad7b0d24b0349478764c6f62fe88e483fb87415b1e327ea905d32ba4ba50397fa0c70425ca096d2311c8b0b154558e2f8ca674b7f71ca9de0c8a93088d9d70a28cbe908285aa453e8379f8d0157cca15c75f28dbfc29e918d2f3a7725b901fd5c757339442272b8e3598d912ed8c10828fc7349d1da35ebaacc78ba86ea2feb693b2de08575d8ec8a80515b2678034c4cb3e15920bc6461a3eec15d9f7c19db25ac3641a2af7839540cd2561645682683c9e16381f3c7eda13f44ff9d6f7ad00a8396d92aea2ea28dea93040360a0de2b389e3b93340a145e97c0ad1a45e8aba6843a4499fbf9a31ad6dcab0dd9a87ea40990c30fc82076461d9f27e407bc86df402f7b38797805c4f388ea242a50f5afc343005ec4fa3d15f37349d5201d60204b3948860efb12aca466de2e07da20d2bee1ff4b716cea16ea40c035b6434208394ef47618c9a05b36b21e04420cee3f04ed87c1f371c2454a90ea9fa4981de27084d981ee935b3956f92c2d9d41e8c45932a7ef1260ee236797594bd3f2617f863d45bbb60820882fdad924cd6ca6c8bad6af9027a28bf69d90ede735d5fe94327977bf453aeb327cb99c16b1f87895cc16b0a86d88b40959d9afa7d5dcafd282cadd2eda3b6828fdd574098fc2bf635fea1852b1d69346c936fcd4e7f0c3073514acfcd9c7330ee361e81c01c84df3070615eb2ac30aeca2f379dbc4f11d734bfeb6529aec25a20b26fd29bb71d9e56779e362feb0c4f8e27798321341a8e8f3d35e67acdc479c642fba17ba87677f815f1f8d15b79cef7e2533f16ec1626277ffa230fd678c3c9b12160bf78e02f78a2d70981a87a4e711c1422fdd536bef214655ab3385b4ac07f1d329cc67e97f5e1c25d04d7d26dfb744efac8ba4787e1f213c4a09145b0bb93b6a04070876e3c7f4e4f0dffcc247525f02d8114e0c8b5228a51726ea1a26ee0855239ec8f411387f77f5cba25a65d6872cbb34a7aee156f43c09b0b5b48fecd1a312c270ca2b8b5482fd832f9099c6438547f93cbf674271b26c23ef795ef582e81a36c5cf6a2564e10891b4ef9b29fce765bbd37c0fe30c95a81c1362399c4116280a46ff3973b0b348f98fa741e42fb67e0a4139da4306469e1bd02d119d28e116127b2be924e581533e1a290e3fa117c0f04d0738dbc69707286e5abfa8ddc405400d1f696a80b5aab65c2e70b13f6ec3ecd951208f975b6c80078d8e484c770a8f69c998648ea766a404486515637864a0663b029c14f33a2bb8a60e2894f02c5c82c192c6c2bf9f20eed8e920b778b3af947df672a8275836e7dfed4cbf662ce92080ab808e197dc14b88d00cb6319b4da02c9807078e38467a7e8d310d9207d13c61a05df7bf7766e5c4e5094f91f3d6078666ff1b7f6a02035143dafaa501d47fc044bfb43d453a18f286f09a28b99c7ea1ca9a3c035e7f2807e8431b660048afe711b39e0ab2714be4cc16e02bbff9485034d9c622be8cac586ac080ac1d76b5418f677e6a83252f7940af0a5daa3eafa21b4c7f812c8d2bb376862d2b1479d741f8dd2b08a6971f6e1a1583b315f6467763dc2167787ccdfdb40672900fe95ce635ba0d06e4ff638f6574dead7ea0290934e40e43e210026b650ca20d0e2d75171be19878f9882d0d1810cc8b6f25156033f014b18333518a1e57cff4245a086e4c21a79596f44871a4665222d28d62ff5b82f73dadab1f20344e2a301233df15fb850fca4f43366886b60dfb0c148e43f2c8e5505761054f031e45992e530a393a66137414996546d93ad7b66a0a86b52f547548eda7c5e9895524b511e5a1c458aaae8d6634de11003153f68f16206876b45e7188e948c3f1daa18525f9048ecbf0206c2f818b3cd6c8f8cbf4f3f3f53408ae779b0a870b51792546102234ba05de1caa383cff3c80772d58d6bf23c4ab08750bf84cffa0e9bf020f19d6a7e461d8404d52ed1bfbb6eb292abe3359d175170898a2480005f9db1abd0a8a310ce3514a6db37e25d075b0f1a0822d78ffeefd3be094301f2c1c6febba07df3bc9e983118eb146c90b068a7c634fba08a71f7e5972f225187a78420bf21a477c7e545db56ad031125723ecf029261e9167e4ced9243047061111c9d89002c6f0d4c6238de97e12c43b89611e498ca6f6d8e2c1d7f04f3424399f4094ab04f76c11ac9856f9164971c2ed828ca4ad43e26981c1132b1289800c2e5f0d5e1c30ca44cdd118597ee0f62df250d260a1eb744d707b82b118b985f1383d477e12acc2957c37c491c396614b2ad84c3969ec1ec4f1c7581c3712a103196948442a6907783e4ddc2a1462e6b13a13f9e217f4faf0361ce35af4ce7b349199ca6208be62a56edd8e4c45d7da43d9fc5acb3ff2c3a1e8ffce95fbafa4da21ead9210ad32d381443055f187a6cb02bb24c4c702af499b4de82124499262dbda32d5000057871f9cc917de7237618da0246598a125df8149b1e1dfaefb350506237b8280517a3cd38ba02d278db56960f92e1cda3d667cce290550ef00cef68cb311c28d981009c19e0c73a175e1f8bc43e7f43177c46940bfb9629b3dc8bc2865f5711618c23138e3ed27b59899f18792d4f410e9cee41597751d50144a0efb7722424247808d0c3c7605a628f75f7b33cee58e4a298f8db7e3556a42de5a519523e19edfbd5551c2412ec0d91b21065eb911c75f0c6580f76fe4ae919dd9e20d90e04ebc9b1779aa7db5c235c15d4d62bb0a614c2ba0ae188bb90c08929ac3425be266e6c0828ac369fe62ef6e37377a346655bb964fcd07085830f8e31f66c247e04086671961a732d6e9824f107aa9cb66132da736c241a93091ce4a5d0328775161a9a5cd0a7510adcda72d5380a8e4261b99479379f9ed014bb36aaf09886352d1750492991f7b968fe80a366b41edbe23fc91064a7b4f8279975369a805a2011d4255af3918a920c857fecb08ca970c3bbc183f7af6cd4ccfbcfae498429d219e4de0ff7b638d612d8a932c88edbdaf91dcda4fadc0dacfb8fd6d8d7a6d5f117feb2e63aa7b37005690a183b7cedd096681e4f472b36b0371e87462f5ad693052dea498ff2ddcbd3bfa0fe4e996a06b9790c26f6870dfc3e18c88981d8f8c214fbbb4970ed74213912e132feedfd355ba5273b00d34bbfa521e5126ee83ac50c6018d032e9b1316bf07d5f6961424bb63041a2c28c55f1c913b15b637204b41fc084c1b1a31602652d016d9178eda06ee7128c09bd600a10d18fd526163f0b081047676d393073b466e12d85711f61038a03e57ab4882971ac99b0d6b2bc0e1b9f299e58527f0f16aaf6871e1db4ee3c5dcf014c2dc65524fb5ea32001e92b2956a53aac0a6512d222970ba9e533b4ef412f10c7eb6d34924f6793bc34685402cdebfa8dfc72a3322f3dc298df0470a4a14b65a2cbd0cde85b0b03915631702e5cf7319251934efdca0c802137e7622627764a0f62e35ddd4fc29ef4624f36221330a033a4d4e45bd32d0e7515516aafac2543bceaf4d88c822a2807c777662cad17ca5faf0c64b97712ae199859317feb0b7a23b9e486edeb09299f8ec8a22d1fd21e145147c31830b3d2a93407687ad92abd6b383db919cd235516e7b45214e830bf52047cffaa97e4fdbffd6cf7cf874d4e71d9ccb92d11867be2577f4d1b61347937a154f0694e2db859286dee84b79269d8b9916562b17ee7e0b103f7068e0ea2e5d9262d13e0e915ceae4c80138a00a157779751498bb2f8f0cff30015fd049acd6daa9fd01a052506bcbd467d78f415b6b30de2ae22f9927017a0c22917f82f9b9c05bf6547479aa55ae683aa7457eb8bfeda7a07e913307f4760440135b956996aded6319d9aa9abe0f76ed72488a3e22473ef0cde7c3736429108095e21cc0b47f3bbc9a0a1da00244efe410bdb1bd733792978b3e361144c3ec927c7cfe7b3fbc8c17738558bc2c90ce2ae06cd4ca92ec2f2ec62a5421a5148e62085eefb1a3092518429b7aeaaa7866b65ec7b74cf4e71696633f53f3ff622592a632e91d5af2332983ae86a98559f65d8f82edd814b7fdc257a8502ba0909071d670f16a22cc60642c30fb9fb7fc98474649efdd11acef790c632579810fbb80f8759feceb7061413e6df2f73cb040cdc5381d4b538866d384c44fae8b3ab330ff0b21753498352e5546550f9b114baea0ba201a86b12ad1d15d04ac3b3057507f7640a2cdb667262b3474a10ef58cf41d2ab58694bb562a2cd8feeab13fcdc1ae46053432cb7c5a671455ef7d8f7d8854fd13f9aad973eec42d9a27d2573162102b926891d0b8b81ff1ec89388965457c077b5e77106d056476a8d69cc035a2a6236cef525b4202ec9b49a7a24d33243c0fd4a57372970a46f7bbb685c8f2b2f02c81eedc45a4ba4ecb5a1601d9266daa5b594dd00dd8c5ac2c98bc6d1397001ccaca031b909879d6f36b49dce0e71c0a1f552d908dd7e18c37b2665cff1bb17fc40b5c7c46fe46827effb3dea634f7493abcaa3f21f974122b393ee3f97340a901c45b90fff46f3ee3a2b88256048094e6fc5d49b8ec566413d651b4bfd481f8bea6c3f158d26f2814a0a21165f5ce25ad588073f7747a302d40898305e5d0fbb41e6583ab90ccf4ade432ec1cf063ed319eeb2b6586abb670a4e8ab4f5d3207aaeb87540df8e47a12e79a8e73b6e9743aae8d5e55aae5da5fc551bdc8c5f150b1b55695d5420881d1aea8e1752398bec30da81de0b0601121045cd1dda9f19f7b5d2266d18c3df2b61d1b471737c2add361519680c8d0c3c151baa9a9c5619cd24214810f337d33af7a5438cb1cb419f4c2e4d5a0827a0e9cf91ff2947c6000ce6c8b213350402828339d996c44c2e7ac96b0452a829022fac28bf9c513e1701296727556febb60ef2acb478eb6d9a0e1eba99466e98aae13867404d352107d66c4708e57fafdc69de80c7817ae046f41cfb2882d3babbf5139a9ae4faa80bebdd2ad2e68bec5fcbbff945c5e7fdfe97bbc3093ba2b1f10c60af59cc60d80e5b6a0eb2cebee9e3537a4eb9f002c108a26738f9bde875b7749a07bac29cf527959a05a688c56c9d390a5dd346626ba04a0363646e6967033b9be18399be09288a4cb2e5a0fa4d656881bba6ed66180b6f4c0cede4ef98e34c5b0857bbc95b2f58bb87ee1c1fd91b5f887521033bc3155654bf8ac1a8c05cebc1f8be7e3a6917485cd6e95d67dc1c766117a3180fbbddaa099949c5bf1151c1cbefa10857b62e9d1116130f8343f772e091058db76e77567417464b0ff93d4585abed3886971f259252fa965f51868653047aee30d5e15c10cec90b2b99109910042dd655cb221cd2533b22397a15f8a4476ae6fd822077f56d3802f01d0b00aecca818ddca1f43544eb3cdf138b816ff5895940945597a7655e30b80c98616ef432c9da35323c7927924b4c1046711099315ec5d58169732b4b75b0a28bc279de6465a42b90ccf643157c4ba6eb5bcdcc7995ca022f0fe91c0eaf1fa910a775e32c70877ce29e5bde84a1189da233e3ace11f96364467be583eef1bfe52b1649a05a8bb44303db325b8fab5908df7878a508c6154937ffeea18cbf43fbf14200106941b8f42de76932b23e689995af9e3d04c0b37765389d1dbd1ad2fd720fb20bc864c025182b41e019aace2abcb7567da8a2449e1887286aeceaccdebf9dac751432f38e2e4ccdca3ac87ee7fad352d7ff41cf060d830a5568773f292d983ed3ea9c87b721b01df39b09be61d87dfac5dd5aee60dcdcfc8bb862c758500b189a324db3262f51d10e38f6d42a7fd9f051204231a06595de9ce681e9966313df7734363ffe1852b06dd157c08d974d7191399967c251acaea70c1376924e9082dd3d6eada315522ab87463acd7faba38e7e2f45677c6d6e2f93d64cd3ea57f855228b29086168d443b8768761cd92dce3ec5e7eb49ce0206c45b0d6c5633a8f7cfe5239b1d2ef1e141cc5a4c73ebd5827d91ea4feae63329ab93aaf49f8621647c7658eeac053de71515e765e1bf41d9e57fd7688cb8978b70edd88cc5f397c16e8bdae86806e7f6a5181704db97138fd3ad30bfe8f3f86cbb665f4a7de7916007e45137ad2edd56a1985f9a10a3df3458a734a9ecae9b3592e43bac49201782855b2155523019488b64b546a7aafc563a9b59f929ee0cf71f5faeb4ba027809a47ea91bc61156c1995627c4998f6ff7e8f59e552aac13a31d70e69b88bb179167f0ccad681dd077952dd05dd90a5cf24f2e5bc1e1915a996366fbd7e356358e0b92108bcc2fa60960482e8b422993ce58bfb7cd058f8e714f1e4d777dd4df6d94d185bb590e76ec3c2c47001f0726c38f0e9d2063f3ef67f0fb63023bd6617a9f53d4ebef1e0280a270fe757aac0af79058d909b8ae0361301fbf4252ec5b19dfcb88c9fb2e453ff90a2a56614dece9e3c3c8872a5116a230c48c7f987f257cda6cb8190c2d11786ae6a859b224093cfc916d924d1dffd0c72c60f04a7a4b525b9de3c0ad217f975e26a887400711a9ad9f1026afadfd263d3bf89dfed2e10939f0b3750912ca93f9498d6ef6f01d557fdb07a8e1bacb8f971f56788cc049b36ebc9944bc164843ed2f857d5b80c2d0e8a54340d6dc5ea6bd33dbf0197ac869cba99390d72ef15e01f0a8bcf394dae09bea03d0b6275f95009008923effce8d6c3fd71d7d05169b021ab02551c8061118dfe49ca2b249b80e1b31b35da1e0bb2986d8fde91e7e932c864120afc164374863379619a5f7f42a733faffb78e3c0307955c30f16aaa12541ee9edbdf1bbada45853c95bb84b6ae8b55727fecfd211aa981feb3195902cdf9c47b466eed7df5c99ed5c1a9986be4bff1f8ba0ca9028dca194a571ccdf56160d35f7db7b9b546457a3f58ee88f804b0af4c9d0ac2197afe5cdca827db53677c6c22cb123f66ed7c9a3a5d66631b4e2d28ec1ac2944dd8928c17b600462658fffb922dd59de1b5c4e3c596b135e5b3ec648c55487c4a4e9d03d0fb0eb1aaf689e5664411d678754c62a9c454cf433246204e7f0d35023465870ee1083f29e501695957c73780f0a97b7fedbc2a9d8659ee308637eeeef72467516b07db68fb78a9a9cd0d7e80ec1855e2370dc2a3fc5da2c00a6f2178d5ef6791a32ca0f2ab588997a20dc02b565f20a93ce84018b4f5e9e91b64f1b8fbb4451072010a3a6d25e1e1269b12967a77469f708063e9bb522a5f044414337ce58745a61bfbc7d14ec22abb3b904e2e7ef401b391fe764815648a73250f96d53b6291d924bd03aa9a5f58d5e893c14850fa85f01e939049ac3a56ace1b73fe06cf1bf4d1ae95413410c335e7dd27b9d0ae5db94283f75ff30557ee6ee8c2facd4fd1cd130640da57669309ad2ee076dcb7b4c485d0a68146b2a687a92c2645fde606d7e29fcd5920546f402de4ab466d0ee17e4dc11a167a17ecb77d8d34b4a67c18e1509ddf08b7a929b2aa92918a0032fa4dbcd8a735a34d11034f82e6f0c111a36490c0d9e41bb0b47ec8bbee1492876a765913a332f93bcc3dab8210c067becc09a0ad7426250cb94602452a54e1c073e2ca216a5f107ce4cfbb6b355d09bd65957285448bd1289902f5e9570e7aa2227117d4db540b9c97d3ed0f759545a81e4d1011380c194c5ea5db9507c00f61b87d20d1220fe4985e2e1d30065a23bd3156f23347b8df7af3454b8739d379debb9a5ab71dc50af6f215ec2a15792ba0450250004b392546927bd58a85055a05eadb613806d86914b6d62bffc79eccb75a7540a5b8d2e55bb8014d6618e71455a8f7b2f7f68dd008ba1cc25d355b90bc3ba1429fb08d96be9b5c21c7999f266cd03b43dd942ea09c55fb5bc11c6c5a174425c2a14c41fabff96d751937819c1ba20020918f9b381431322ef154b7363dd8ba90e7fec6e1072b896789f0f4822c0c147367c3fe82792879a0ab3caa19873431814955df92af2d824e8f1c3e879df0cc0d8a0f5bd76189cf2acc89a777aba170407d7788ecbb0c719fe9a150ad45a5dcc5d8d0a6923b7b27ef6c6b7d61c8d36551f74a4356e4dd1ebcef55bc96b0f6d4ec261619111bbbf7d01cfd4886d1de53623b33764960fed4f0310cf5bc7c27fbd4cc043bcb52e3e5a5855388cccac4c3e2326086873a0072e2bd03e12c78207d91b323343a2be00ca1d316015e6b7b9ae16dc2e72f35764059963da57573115adac29eaae415033e5500b11b392df0090240eedc5326ba3c4de726767dc7cbb6ce92f8cae01a136f39e72c82a7f455def1235497e0d5e3950864202a2f45edf6b800dc64b74b696f2aba673b08e4a2d822a929abc9e6ec8f99049f780d6281f48c9e92978df5d80724eb41681b3d4d2551149adab600d300405f0d78e43bd382f5011ce30554f420273618ed4474aeeba226d1c60827a4975c016ce132ac2bff671715085fa050d9512bc678279dabc7e2937ae6846d97736f3c49eb111407b8a9e9b127e5e2447d4901fc973a421ab3d22eb9ea67ff3094ae76e2adf6da4a0384008de2fd1fc52336fae0c0eb8a1f44109530dec9ba7bf4d1b3d51afae1f5d20392178cdb6f4122e1d9241d3d5b2630d22170047f6dfcf200ad991bccb04299c0400ae830abd161c6f16fd7f5ad7d55e2a91a91c308686453239d3a527c4b1761dfdcbee18db8f02e8efacf3628e99f32ee6e7244763d56e037832853423134a5f8a5641ae4f979e50ca4865ef06643f02fabe75f0cace0c0b039d4452ae0f365ff529891fa3f371e664b389ef7886eb3030b411db22fc59dfab6170e4a1ec4e136124a10896528963dfc5c96ed4259d1332ebc2b184c375fb6481b068abfa1c8d7b863b3c764d18dec95f55a13b807a1ddca78ef66c3f679f534b30384f7a796467db5f4e8595e0500d634744ca3ede0313c61ecc7c3c379c7edd4a9e711aac6dbb1768dd7604ad4ede42a037cda84d13a90845592175da3e3c72f9eff83911da8d0f3279fdb75c5057a140ad1feba217ff17582509379007b855924ad126cea015f23da97b9ff1a85d2e26178e7f492e38990677a0396678b98a389e5050c5a74ce4a769d0f445746bbff937f7c2032cfc6627c5c3af996ef37c2bc10c5a7c94c2c6ce88e04e466b9c01bcdaab60491533b97ef4a264153289662cee698be8ef26b021adcff3be3655a6de1f89d2271992a5af8f646b8d1d1887b122539bf5ff80b0d73282d288a0b7b1f6b15b7177151794da1af79b8a3265adbe34932c3a5ddc7cc991ae4280d005ed69ee78a1f612d55895627f1bbc0425d4f27aea7f2030c68852f2e2a869abc8c656282a69d91f4ce47b4ac4253caf925dd52ff54d1cc443761800deab6659b954208a22c27c09d9bc509923b5af45933add7cd707235c987f02bc0f609b1ca5a831eb61ae990d69687bbcbd8889eeab15fba07d77666bac222248c9a11092cfa1368f7ad93e2c70a325f6f55bfb9c2714850e968c5f9159bbdc4da5b5845ab1f4b196b06b860c8eca51ef7ee7ea1c5ac6741c3b45eeddfa510a3b35c87046b84859d192a7482e3c7825d060b1598765bead1f4993717136208fa56b48f2ce52669d7c2aff3d0bbd8cf1549ca6332ec3df67c16d599ebeba774d36a3c23e47fb3893ba793a9a2825bf3dcd7dfde5259eabdc59b130726508bf5285e8e462de85cc1fce521dd40ba15d9af05a686ca5bc521d4f87492904e9471a27df178a9c32b4cd292debb3083e36159fbd062777ce3a5cf7e42b68cfe357293dccb10a66ad7605de413bb62b339778547ae76ac7a2e1ed9831ef465da55fa64c661288a01252ecd7b9f5a62e5b8687ec4064245e7c14f1b044bfb5833f5f5354a5e52a118f30523e80fef11c4a642b47eabd6c27c18e8834c0aa44ee3353aeca611d41d5a139741f106ea329f5a4ca9b810877f6a0530f157229cbd931de9be6ab060c9c7aedf12a6d31bd1416ac65e49f6fc1ba4fcc40934eed1ed6750feab533cee8d2a5bf77b70860230d4bcc80e332fd6dc8eaa597a64910504bb9f7a5951e0b1cc5549b084e62d3612f42240d9fe81c6c474f9c91184edd5d3d9b5b9211e72d85f6d4307f89b015d01987a2ba529b3e3de4305c8d91a646ccc82a4bedb2a4907e9159c7c99ad0ff59036d6623b151c78f80b23febf74f81d0a92495ef3b64f19e66ed2f41012533980476096427924dc67d52afd687936db302e8bd625ceac02ee8d96da3ffaaf18d0dba69b142c534b2af9d4c54d09bd072b0abf5dd514fc97ba8887ba0a3cd11601669661d81e87b4a811cb36b178dd20b80a0f2a7d30e3cf1be317eca455d578327e0b8517954ae519d46c43b511ff7b5b5faec58ba349aaf5e9869f04c2c67a30474deb6a73b2ebd5ac1134811f1a36c4b25196a574cc0466bae6b3fcdfa67a40beb8730f67992873081dd969bcb5fc718be38b7f2d905e0a956005ef56acc7229696bbf0b5783493ae613da7d540be2d78baa3c0ae8ef5dad872e5ddde1b4801ab09d3a847d4e7b763d045842143da7dd7506cfea1768cf8f6c02498045a5c12e7e1694b77217b83e4398daa91eede9dfd0e756b5454f4a53c44db1ed78b05f180801c4a0e6ecd9dd3c75364074acb511c8c2388bda38e4082a2f52d456d690044ac311feb298ab6d7975886247df78d1f764d91992ed87babb126fc4260470063cc176948b6830efede55d9dd95863e7000b0d6449779f26cc2fe918cbf16b0b156dc79e15d687a3ceb7f9e89f387efa6739bc8694a0663c4ddd1034e732d82d98654f4ab727422694f967ff95c25cdcfb0a7b67a9ddbdb38b02ff739702532bf9776901d83b68bcd265e6b441044b18a466db1744e60f715828741c53b38c2234f0062231eb92cbd23949615f9564eeca7e3d36a66eb8379ef32d66fadfa4142727380dc5ad292c52c7a073b76f8cd789835175390cd2e01c49670d4d66062ff44e2d186ace9c739f41b6027976d14bfc760f07d82c033b950d9239f066c31719f4e87afcdaf9ee8d0278b99a0e3d18849003d3be00c682b37ccc6095110605f89adb2b7f013fce5b354fb68b2b32577fbd6c48a89bc1217c32ba2023b975219cee35356409db3827f407d60777d58e8348a4b165a09faa7e89660e811f2b093603ded70cd3e08e680830d676175ecd7efe2b0425ec8d5d7927f40d04ad5902b02397c2b75482dafe445739acb0e8ac4e53158ebd6e15185513efe9d06f38251951521b1b37ee05c9ecf7335e1d561617b68823ee6ff207eecb139bfaf3c919fb94c51769dd2fcc89cceb51856e10d9739407ced8a9849efdc2411496ccd72ba430028754d7f3e1d7aa46a8a78e0a0b05709edb0612241c0431cb38d32a8c05fba833cbbe1077cbb83aced24898c91227ae2ebb7c185a8d7be7353f8a0cfa2b1e7248dc2974a6e373f4c716681a6f5067fe446777c3d31571ef38e09783501336d31654c7c5d95a34d5671e1079566c33d4f32cc5195d4a999f56be81b1c9041c23e40a59a05ba40da1f97eaa2f83d737c4ee7767a7f75d44f94b08c838cb26c9c73e59f573dd389732d09fd9fe9eae468c6b879ae3d2a9d2afc712a9330a21f7670683303dee05ccfc96beb8f2d894df04693179e2973cef115c09354775010f3c5156faba3e58bbf7bd54bee5e5308a25e94fe3b1147078b6147c753db4288927e082ab365f6cd4f74d0e850a3afcf01629f899df35a216e1d3adfcbecc0b5176ab93d7221145c6d3f2011852b1b0d5e52595b97c6d75e6551b6c018464068a4250a21e45c570aef973ff978e2d0c3d1f32ca88295e8dad9a6b665faa3700a8ff8659e594a167530a95f3f4327d7210e6b5963fc3a3444d16cf05a888dc17cd53ef517b3ca9f2c4da57eea412b405d27341a8f3da610420f8cff8fabcc22ea7167258ec2e0e0153836c5e3fcf168f25aa7172528b7a7a10e6a961520d320657abd3feb900a60de5d920a78a84b6b91bd86a032714f0729541468bc628b42fb2580aa35a66684f00e8aa78ccea16a6e2c2a76962a2c97d2bfdc28c1a14b1029c7b68587d6cec18c23893dfb394fa183bfe597d4d9f41afc074c0eff30ee90ac83636244fc935d52d9f8c32884d8ff049e073a3dbfe2e7f3a9fabe056db851b8a0ab427bd70186dac196be85cdd9cf6a472db35588775a41d8f5bcf95cb1d2a0a34567b854f3d22327387c2b32d30ab6abce341820cb40065f24e9b4bf7a111bc7a424546ad33f509901289798a8b7978dfb8db35dce1f6b3dba62a1c5ecd1b0b6ebca55130fff8bf52b3f525c4b8b490664fccc47262ff3df8b6042652960484ce932170e3593575f723508e98ee25bfd80c7c9d432d952dcd2eca28ab9b2294cc39122d4d6c0eb32e53e70a7e8036c79c0c0dbd3799259c82cfc1c7d0910d399780e2969a47f912e8b716239eede7888a4f541bb71f30133851e10cafa40d3e8226743bae7711324bc4ec129c85002d3083a2af215f38359d37b8d6ece90f6c000b0d56175158febef5a203c3fbf701312bf3d11667ba4c0409642de183162b2a7fd33f67c8f2c4df85c2749615563b0a782dd38927c24f0adb3dcd67a82c8d98745e75c91c8cb14a48a18cdc21c337d33defe7a34ee52d58f2ecb0f33e1b2f7ace0a364d7b999319eac3e703147b0f0289d13b496a5baf1132e2d69fea1c05498e6412f68f8157fd373a87e35aa4e60aed1ca9035054dc4624cdc293e6106ab5b7636b1eb293919de2f9cfa94f8106140a2fede50519fec44ee3b4d12a01f74160bfbe701af451915e10e836bf9dada21bcde42f5c5f62adcb5ddf571b2ed223c42e6671814eb85aabd89968c5d0fd5cd71293c45f665190f6dc4e71161bdb143eae947ac64357958b27ebfd7b7d747c635220135d07e5dff8c97c02671cc1498962fd55a7444e856319b117c443a4ede613035f4771414a54e3a189b7030d05c4fed5241d83bcbb4bfde04683b10d845642ea853252e0588d953592541017b5276b6057e49402142dff4a01b46e0577963f1d46395fdf2d11092f0ffe15681c8e2c65ed60de0c5a4fb3be87a9ae7795bbfe16c96f2a2fe963c12e7eea0f851773e33fd8eba81fcbd8a3f51081726eabd522109b504e688c1367f7a831019103d297cdd5e7d383882d43a1e392dde72d8b45f74206cb3a4af9dd534e881a1c9c5304e1951778074af2513419fcda57d4002f8ee3fa959e69394fe07757026d016f186a85c5dcf4736885598c99220b8e32c16c41fa25e9757c796abf710612a3fdac0fd88526f8fffc53774f41d7d5b985ed1af86b398aed2074aad96d09cd2356845017140ee303a82f21315d018a149cb23f4098c5096334152791f128495dec89751238d8f03030911d3d2975e8c637bc65a3c308ee81ee28022f001befbd0c957929a6cf5e6682b645d001a3ff9a5a118325a69b543d179f878b2d0c7808b8477eff26b1c176fd463289dddecf4765b048bd6cb82468bd00c5fb42", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e44107940cf3c3a18e9c2b8af0fc91a18c91a709c00a8093bbe606a2e9fd472457bc00000000000000000000000000000000c6e378e01efa1cb216ab0ced340b44360000000000000000000000000000000097621038a18ea02190913636079ece970b3f37898c93078120c11921f007446ceef9867e6da4e341fb3bf8991edcbd630000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000223de00e9e865ef8e388eeef184b6f39f6bc05eb3d0c54021d2345bd1729316ef17122a4b371cc1b35e37beb851894eee43c8b48d01f6e641ad9ba6e94da997fb167714b66b4e3e97a46578eceefc9955b033a18e6f8ec8add6f96a917a74414421b654b589d9ba8107e6007b5a8b71a20fb1deff0cf22904da6dd4303e734297000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } + }, + "test_exit_code": { + "crisp": 0, + "folded_export": 0, + "enclave_contracts": 0 + } +} diff --git a/circuits/benchmarks/results_secure_agg/integration_summary.json b/circuits/benchmarks/results_secure_agg/integration_summary.json new file mode 100644 index 0000000000..9466c36d58 --- /dev/null +++ b/circuits/benchmarks/results_secure_agg/integration_summary.json @@ -0,0 +1,244 @@ +{ + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "secure", + "bfv_preset_subdir": "secure-8192", + "bfv_preset": "SecureThreshold8192", + "lambda": 60, + "proof_aggregation_enabled": true, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": true, + "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + "multithread": { + "rayon_threads": 13, + "max_simultaneous_rayon_tasks": 13, + "cores_available": 14 + }, + "operation_timings": [ + { + "name": "CalculateDecryptionKey", + "avg_seconds": 0.036223306, + "runs": 3, + "total_seconds": 0.108669918 + }, + { + "name": "CalculateDecryptionShare", + "avg_seconds": 0.156799111, + "runs": 3, + "total_seconds": 0.470397334 + }, + { + "name": "CalculateThresholdDecryption", + "avg_seconds": 0.234711584, + "runs": 1, + "total_seconds": 0.234711584 + }, + { + "name": "GenEsiSss", + "avg_seconds": 0.220404125, + "runs": 3, + "total_seconds": 0.661212376 + }, + { + "name": "GenPkShareAndSkSss", + "avg_seconds": 0.202163278, + "runs": 3, + "total_seconds": 0.606489834 + }, + { + "name": "NodeDkgFold/c2ab_fold", + "avg_seconds": 7.089881625, + "runs": 3, + "total_seconds": 21.269644876 + }, + { + "name": "NodeDkgFold/c3a_fold", + "avg_seconds": 57.963919402, + "runs": 3, + "total_seconds": 173.891758208 + }, + { + "name": "NodeDkgFold/c3ab_fold", + "avg_seconds": 6.906346736, + "runs": 3, + "total_seconds": 20.719040208 + }, + { + "name": "NodeDkgFold/c3b_fold", + "avg_seconds": 50.328348527, + "runs": 3, + "total_seconds": 150.985045583 + }, + { + "name": "NodeDkgFold/c4ab_fold", + "avg_seconds": 8.440406458, + "runs": 3, + "total_seconds": 25.321219375 + }, + { + "name": "NodeDkgFold/node_fold", + "avg_seconds": 15.28584993, + "runs": 3, + "total_seconds": 45.857549792 + }, + { + "name": "ZkDecryptedSharesAggregation", + "avg_seconds": 2.887353333, + "runs": 1, + "total_seconds": 2.887353333 + }, + { + "name": "ZkDecryptionAggregation", + "avg_seconds": 47.438696625, + "runs": 1, + "total_seconds": 47.438696625 + }, + { + "name": "ZkDkgAggregation", + "avg_seconds": 19.61153325, + "runs": 1, + "total_seconds": 19.61153325 + }, + { + "name": "ZkDkgShareDecryption", + "avg_seconds": 22.058676235, + "runs": 6, + "total_seconds": 132.352057415 + }, + { + "name": "ZkNodeDkgFold", + "avg_seconds": 146.017955028, + "runs": 3, + "total_seconds": 438.053865084 + }, + { + "name": "ZkPkAggregation", + "avg_seconds": 24.53227325, + "runs": 1, + "total_seconds": 24.53227325 + }, + { + "name": "ZkPkBfv", + "avg_seconds": 3.559760472, + "runs": 3, + "total_seconds": 10.679281417 + }, + { + "name": "ZkPkGeneration", + "avg_seconds": 70.937173541, + "runs": 3, + "total_seconds": 212.811520625 + }, + { + "name": "ZkShareComputation", + "avg_seconds": 36.93532184, + "runs": 6, + "total_seconds": 221.611931041 + }, + { + "name": "ZkShareEncryption", + "avg_seconds": 116.868327526, + "runs": 36, + "total_seconds": 4207.259790957 + }, + { + "name": "ZkThresholdShareDecryption", + "avg_seconds": 106.505539597, + "runs": 3, + "total_seconds": 319.516618791 + }, + { + "name": "ZkVerifyShareDecryptionProofs", + "avg_seconds": 0.12560836, + "runs": 3, + "total_seconds": 0.376825082 + }, + { + "name": "ZkVerifyShareProofs", + "avg_seconds": 0.320742583, + "runs": 5, + "total_seconds": 1.603712916 + } + ], + "operation_timings_total_seconds": 6078.861198874, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { + "label": "Starting trbfv actor test", + "seconds": 0e-9, + "metric": "wall_clock" + }, + { + "label": "Setup completed", + "seconds": 2.661124958, + "metric": "wall_clock" + }, + { + "label": "Committee Setup Completed", + "seconds": 20.166480416, + "metric": "wall_clock" + }, + { + "label": "Committee Finalization Complete", + "seconds": 0.0012645, + "metric": "wall_clock" + }, + { + "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", + "seconds": 162.089313, + "metric": "wall_clock" + }, + { + "label": "ThresholdShares -> PublicKeyAggregated", + "seconds": 604.932522792, + "metric": "wall_clock" + }, + { + "label": "E3Request -> PublicKeyAggregated", + "seconds": 605.453073833, + "metric": "wall_clock" + }, + { + "label": "Application CT Gen", + "seconds": 0.353204792, + "metric": "wall_clock" + }, + { + "label": "Running FHE Application", + "seconds": 0.000809292, + "metric": "wall_clock" + }, + { + "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", + "seconds": 50.493282, + "metric": "wall_clock" + }, + { + "label": "Ciphertext published -> PlaintextAggregated", + "seconds": 159.9251575, + "metric": "wall_clock" + }, + { + "label": "Entire Test", + "seconds": 788.561521291, + "metric": "wall_clock" + } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000ddc537f44b53fe45e000000000000000000000000000000000000000000000005d2c364993f27cb360000000000000000000000000000000000000000000000036a97352d8068108600000000000000000000000000000000000000000000000000001db8386de0ea00000000000000000000000000000000000000000000000002bc93d2aa2c401600000000000000000000000000000000000000000000000c0d14f10c5fbb573a00000000000000000000000000000000000000000000000c707bc7be0aaa260c0000000000000000000000000000000000000000000000000002b67cbdce2a1e00000000000000000000000000000000000000000000000cd16482194b961ccf00000000000000000000000000000000000000000000000b86ad9a1e323001d700000000000000000000000000000000000000000000000de1eb22a6bb20778c00000000000000000000000000000000000000000000000000027eea9bef8094000000000000000000000000000000000000000000000002fc183d9b404e5b4f00000000000000000000000000000000000000000000000f8385a2178429603000000000000000000000000000000000000000000000000cb22ed841d6eb43670000000000000000000000000000000000000000000000000000cb1fcdefcffb1d74bb7961ace3f2f70c4e830de967288c03b173bd5158d3e5cd1d7fc55e865a03cd83a7ec11ca7d54aed446132af02f106c160b605361787022be5c8a7d0fd01c35e411edcdb143c708c052dfdedd4e54fc380814b419aa5341068557f1e98511bc88a7061ee755e52288824c391786d6adf9a09090ebdc23941b5572d247e416da2f3733c9efb99ba96eb277e14f3858272621b15735c4cf986eb6f9922ab32bb3beb4a44733ebabe5d94219e318b866bc934894e61df208758067275f6a7e1683d765e17405f0e76a58240bd4c830d911f26e464c227e57d2e92c9a1e85210ce7ac20d92d0db6dc8f9b9f63e554bb78536b2301522f2901d1fca0b0adee9402b670ab72f4bd26fa49e85c45951a984d4bee6c4bf2070544238ed41f1adaeb1ec052c80e75de7cb223007b5f194ed2f28d04cede81a64ab07783f001b76d942d16c4d740f877adb2dea5cb85ea18004f92f4f00e54481fefcd1115186ab43a0acfa1ca0a228aeaacf7ce2429f7a4e9581a243c4029a443ef72a3a321e1682d14a7f2f2757df78eff6768d1f653b2a2994b47864fc4f80e532b46354245b7a02a6e00ba0057129b07cf30279612ab0a64e41eaee79db02dd32ee1eae16f626201d6f39a4950369f70b76175c6dce4ba02eb128ebd52cb75682acc63f21b52792eb92c25245ec066ae29f6a9b69e7980e89a66b0e8ed0b8461e1a3cff3a41629169bb7a7faf7b158af57ac2a8814736fa48f50908a1cffc783e3dcea358a8dab05d4e8b1394a69227bf3fe6686bbd41b65ea8865f9616b853e55d01c263cf4601d3b429b3fbdbf6c3ccbc8bc7613a55080d4952205bea66a6487d50bdfdea2da1966d442fbb7b6d5de94a7f1f66b8f2be77d33671744de4db5ae847949c06e0509fb78c99816ad278049888bb597f7b08870142bb7cac68ad7bd9866571943eb26f4a3efeb2de8d56203502ad26bf6d582fafe6e893716505fca3da244cc36a329057929704d19799de43956ba1863b26da9a90cacdd883e5afe90272482e3831652a8b3992509c56f1c37be3f7da0d5ce3f5dff03f2f28f9f8971e78c0e257129790bad219e62911680c2b8262c491a19c65a69e2801de14920a8e49539f7782820f32def40017e1af00ec6d325e4c82b26d4dc738596294c15ecec975ad3b00ba9388f8956cfbafba1f6c76a2a9367c27738125e6f97cad3118711c432342e01a4efa9335931898b9d34cab011ec52bc1f426517facd578e65d3937685f8192b4abb55aaa96dfa5fc246f5bbe5bc9777f78e01e8c44a8fdb90646fd71d0d641738a84b0f2d2d822d017cbd5c5d9d7363eeb71b6098d280119b1ecf9b90f4610307dc478a39d30eb8998fad0001bd00ff2bed07374e5dd387830c146323519e08f35ab390d4282810ac470f17b1ca6326ea2421d6119c6742dcaacd76c30286282dba21e7d7389bde163c696622d88838635b4a546d17f1600c18b748c9f72c2553375c44b53f71a52bca0c87762df6552408a5f4500965614a5b03d4fede551658351460482c296ccb4154aab50e4ccca5cdc55fa75423c3d84c64f70518b208aa01ded1676e0474d468ad377ca4d2b89ab3cb3d3963de0a6b13b1debce552033885824e78ea0027af972aa760ccfc5e7fe7755a65a6c6e4d532a33aa47c39099e1828b109c1ac9114251607526ecbbaafb83e8fe2efb0c5388ce62ea3711b2b28c132f05e1a371ceaa48cbb778b4db0530d863efe66dc5a00840ee3bdc0f71cda948b42a5a7059b54dfbec5be13a982ecd3d08e07db4e9b740afe7b136f6129ebc34c7ec5021908b52d65363cd12854f10066c1a0e09a9733d4a304ed41801ee2ccf5b72db94d42ead97f746f5b92eabd9115d8d3c47c7198f0c59f61c82c2e6cd40132c27b3a7014a3d0404857ace260b2cef00871951e08d2fe4af80cdc20b67b6dec13d790f840cd45bc09fb36f7e9edb1576aac1fed6309d69d9b4bdc23a372afc89b2b8c7f8f0b4d5a8a9964d817ec367098dc3baf84a14233a8b24c160d1e3cc9588f8c29ee86bf38c8467751c54dc206ecd20dbc041b9edb0efcf721eb824ff79a781cfc29baa77eadc827e1faa60d2ca595aaaf41b05f47403df5054d2e48bd550fc31e258c9aea79d587deff0e66ab7e24df5ef60a675dc0d5d61b9ff4f004a2ccd4320ba46ab42593fa9ff56741d268418ad57e8cc69060e112173229ebb2deb9626294c2c341417b9274d7624bf0f7bfad9d66b10d28b8a03116f1913cce922e6dba33eb29a98210fb69f529a830d6040c5f0117608ca3ceeb2301f5fa825d225c3bbc88b05c33ea0e41eb014c66441d5a9947fc4680d35e421dee79f2143d0497c9179af370b754e0224e003d05fecab656f64a823fefe44313b30bf0007ad379d1ca866ab25f493e8e18900fb389ac5415f67156120bd77c1d83e3909193ba0d2b16ad19811ceb4ad3d742b50246378e6fb6e45468e9008a1775f8bde9dc6a8231c8f474efdd6f11f695e86315777cc234f2c04bf87252c12fc8850ee7143a98fb139e12b5cddcb9215993cd57f93020e31082160c1540fa0ee8fa70c9c36ae886a7e4a52840ac356a9c28b4a0cc432f0a57d049d9c60ea52f9afcaa6d4f8d79c21ae5b04777811f9960f0fffd52580320d0fa196009634e0b20b84cf5dda38ead8c9dfd830c5aeba4fc6a0d4ef749993b144954888993560f5144b9c881f632066f6c63c95f4066fafb419bd9601c028ca2546fd6c3efda0b99e191422cdca5d0e71bbad1c8f5e358b973d75042b1dbe0d437393ada02fe22b41dfbcfc9155f55bcc64ecf82d0a67d810009043901d592b0a810c89a910b1ea0e956bf5eed9782a9ee48cc3a97b989f1ccfd38e3a2c24cbed0109ef89d151c96ad5afa7c6d2e7219744a1338debcb9f41485d602f9c2cf30efb8f22adf390291fcf0d2a9ec8464d8180854056c10dadbb17a051f9c2fa1389962ae877246190150bc8fdf39bb570e596c3ef14377483c735c8325a32b2709ca1e939c9d3304855cb7c5d17fad910f9842c598b174fa9c275096076e45af017ffe4d0f9b41129d3cfe4ec19dc4f72a61ce91e290353e9d411dab365e3cfc43583fb9c8be7c2e0f7417e6953d9c8116c57bbde9964dc413f454ba813aa314c4c9dce76b00c9136add93dccb1318d338c95faf456f6ce48ddfea69c406741a3e59dbc207cc9a190806c021ece63a687d90e4ff7fc6172c53f1b23827409310b5a44f62fa60292055a94c5a8d3471dcb94e967000cb2decbcfa85aa6bfbe4743e08a1ee230ace1efac82370ba2f09fa77016274bbdd487475e100862dc8fbb80382bc69de285c0bd0047ba0aa8ec022bc23168d5afa5b1d3a1ad7bd4b9c2ebd431d3bbce150a70f29dd3aada8fba9f7cd5d5db945f024a9dea0dc51a27752f0be20b4483ed1f5134d34dfc8dda93da80b5cb0135d3e4f1b3acd5895afe9f691dfe5a41109caf52391b88832ea4c3d2b8627b03beda0692826fb896c237e415c9058a78be861841bb561ac86c2c8800167ddcec4067160125bbb52a30f0a1c7a8cbc2cf4e1dcc107d74dcf175389be3287894d9a8ca93a7f13f4b482f4d96b5a755388c175b67f077bf625750b2cc8492e3829624f6acaf7206c352edcb0860416f30c5900359518496a52f58dcffa59213f02a3968e939b821acf25c1d54ac8e27b5037799a971a45440c2bf0c8294f291765bb3f195dafc6cd825641f439c909de60f22df7d90570f8581fd144d8e336e732cf807c575696fc5974beb6d327d0f4e50fdece5c20104dda0e51dcec48d30f4ffe4765a5fa9f4a2bdcc2ba09ad027c2ebe4188cf02c493f0beec1b3aec852dff73de5ba65fd4704359f4e7954158bf624d937a0b09c929bc6fd95c537456c59599363f86fdfdf5b7d2b58ef05fb1b62c4f93945e1e9b7f217eab940cbd9d3dc027a2614a0e0ac55d6a5f4fc517c77a610772a6d5067e09832583efad6905bdbec52a599aa83ba05aa495f7cff70fe7fef1e8f9e322b71ffe815a45f39d91a5606a09f8725c76214aab8c32bc3a2ca3d1c4f6f342210f1e70ba357d8e93e48c3fb547aba790c9548057837506e9d4023e026050ca154cc3aaf0c7d60316c7f56a6ba004273b5c1a08881c83cd592b770e063f5db6194b4543e50e6b2d4fd1828703ebcd61f0e0e8238c7c2907f640ad2245e6f8df296178c5b6c882031dd6f0b4769251c1cc7243b4137447d868802cef801b2b7c214299d10918e2da7ef9a09e2bd9e11432798d3054d50e494c0da81d33d89a2607f681bd3f241f731f09477538456c7b3a0157ce568d7d4c828a4c24dce4e2b10cea115a9802c9cfbc41c26092502ace91f9937f175d77e3f165ee57b2ae6148293043f2a0c21b1110c61e1d48ebd32277db4f3f8fa1a44148cdcf7ae17d901310ba46c7249cb240b5c8e89ccbc193ea405bd845baf1616b538abd45acd32e250b0eb91ba32c5f67e81b79cc12e4b202a3a514ddf1c33f55d76a78dd2a6f431216cd9459be0440312a230538e2c08e9eb699323c3ea330fca18c022801cc6bcf1be0daec57bbbd18d2b11084198ea0cbe1d5b1f16f4c9c2f912d01e6f8b386610a28f67f554cd1490c99771a65f50623d2fd504f9c4ed37c92011ff0b00fd73f2e96733327ef59d02451b02b31243d6de810ab7d4bbe38cc5c4132712f9e649b292aeed91c210ad7f26e8b460f9a2cbd737062f3479f6790b0d85e6b128901da2c988129a0ed53916b48753bbce885de1a2f74afef8cf38b2dd4d8aad9fd5a7e13f32b602a56d2fe14c56a40cdb217be8516b43d718ae6218a02c8489fbe23562cfec06264e235fb768619a1c68daee1f5343fa2fbc9dd496b59aca837eafeca1c526e5ad567e4a3c658bd6de68af23a0a436df9f7f3f29f0ff683cc517a04580fa8bd2d49661956354885fb019f0cd95b7b7db7d8de723a7cf201babafb415f06da7a7d803e8584a58a41c24ae54f7ea86798affd2ec39e5971f63887d3bc631b8ebd9aebb270eebe21f676d194077a0492a039335bbc974e1a7e8e5e3b187b005cfce57954aa83f4f1373b857dd0e9108f15b8a3319cef2baf99a947447a6e0970c4556b9cf6a5f7c9b1c4a16e1327cfbb978db10ea891a2ef12d50a37cb040ff5378b30441161af327ce465ca56b4093e3eda29ec439c89dc32425bd33a6b0259d8d1d4319bab6eda9a614f0f78eee496b69ab9fdd9aedc3391378993c459027d87ba74779c68221b5b1e24712803e9f034793753d49d661fcc34af88b67a2eb483c76fa9350617c43cb676482acc4e4516cabb406264d4e41fed5f4d800b197e44dd76820dc1d207e1eadb5fbf61ac15b6dd1c7f34dba0210c84606971fb2094e779db97d0fe0250b61846c330025c5c951e7ea310f0482708886d6288c12527abb45cb435c1579afd6dea2799b69e5298f3e6325f2926583697b5e2b5e21515e902fd878d2706b6377e4a86dd3e75e9f1c6d5358b69898d7579e05df9ea09955053c4b5d8f47e09260b20094fc317222707a15fcad33d54ebb0a286f20522163de47ce56a75b960afcf3ad78fe489bce9499c2aaba40725da1d0d3753eb23e9215f54dc85bdd4633ee04cc3feac16e1961804fbc82b69144f0d02cdc09b08635aec4f87f85d0f8c2c235c257389f68de57c17570ea93bbdeb89aa489c7e2d06f6ddde1845c36d494c75adaeaf4e7f71ab4c3ddbff29c3a02b1d2df0bdff2fd17b65e8f3ef08cc0bfc3298b30a9ecbfa8748e5dfa71a6c479b7c5a2c68e60b8b5d197e76d358697acd62ca4d0f0b4ab5c7a1a6947508e0f1c5f9af1a4d2402ac2c8f342cff48a42c6cba919a995820f017a9215ad63a7cf6b85aea4bb44a2ddd40c367f7b0d4db0755f1f0513e8524e2fbcc4ea1cc65eb312ac3a0ff85f4287e803ad96215b8d15abb6a304a8c0f13bfb0d673d9b9a188e8bd6b56de3ee426c3ccbd67dd0b3996a9719948469dad9f074a13094f68e012cb4f6dcc18a9d92c504aaa12f96b40e0dcc1822332351be7c39d99a32b099abd4bef860b50e58a03bdb1f0fdd97afd4b44426fbbd310c7162282cb6cc5681a5d34c8792e7671840661eb520c57338c07b2b400bff64f9098a66d2aa940eeb9dd8d080e274620b022e6a97b9dea474a50239371cc56bed9a42501ee62a633aa6808daf395da3c8816947be7efd0e3c76672fd721740f8984d4c0529770f90560ba8a139c4f7ac6523acf595db0a22ad14e9552813447cb062fc03588da0bec34a24d282a7de1889063cd32f9dba2749bed618a058e3be4c300735d339e3f39afb597c470c287c3620c845653d8bda6ff9403c4e297a697a09dec0e9eac2cc5f590f152ddf12b0bd1f0425e8814a418457d8ced41d0d54e10057d8fe626ed42f6a69a7981809fb310a321c501eaf6f32dd062bdc21a63ad2b9a509a0d7d7362972449fba64911a3f0c850b147619d6f54f6437612fa2c18d1bec1016c640fe49367c05a1096894e4033f004ac462a4c4fc2aacf97f4303834e99878ccd540822891a8cf1baccc0042989e91fa78091f21264783ee35463fda706bd6bf6c3741cc7191892050c36c611395fae2e9bcdbb8d8d4a820a1391d5b4395cffb38f1a23d3ad3389c4e13e601842eec0dc4f455637e9912489865c89a373fa4d6e0cdd95b9a36043032523cf197a2f7a626193b18e466a3f5333ab0a576b7a6c572c8b67352f13d570809d6d0ae6457f3be1367321458a0e4cc1453d02460bc831f9c268488ad05f1433ccd0271ecde0885fc671c03a6d62646ea177e0bd6d7a3c579ad57ae0cafa74ccca141c15b844d1c1ab9deffc01df3f7fa8396ac7fa4c7576f689f22949b2915bd6c9161b73eeade29e2603da9d7d86e6cb95baa6c8bd0381b284378b2cf964fcbeee2d05ae518781466b3302f7561b144d6ac87de4bc42056911ca7f594555e4a5bd2c228205de015357c2903f7d9172e9789359250088e4797c458c6f439f57ec881fbbca82dc4ec32547233fd8b9d0f464e8c9cd656e151e6e7f573cce989e8335259537582f5e8096dc5b6c1a2d5f1941b9d01761ab23811b7e86df9d9a9ff7de2304576421d982b42b61ddc6148e03a73deee2e734616c495af2eb08c76cce640795dff87432b303fd8552a8e0bfc097667dde8f1434a2a4458e7e3e63b2d2e31fcc077096bd6aa45ffe9c70ae160cb4d5e2d28854bd889ba3155ce0ef9d087002539dc1ca5eaa4c94ee4f6b8f8199a1ddc619a061c261bda03b79c8c1ff158124ddd94fbe30c2ba65f28ecd47d77724340ec1fb1143c150a014e2241b78c8801364f1b969518cd9d8a46e0a4b2526680351086bfe08d3a0611f5402fc8ff7a2253c9dc9121f43c741d9059fdd5a5bfc68b8367576fcefeb2103d8f2057614a42e38726adf28f52dbc15715e283dd4d3e315ebea3d87cb5681e648783c45631b17489def600de32fe48770d6f8b47af009996c343952dc446322afe142c7a0ae2fa2ae3dd68945a1e5b1cb742a36aad5bf6ece555e28863ffcbbddd985b201132a1a3a08ac58724b1cd5330dd46b18833b3349816a5dd935d98cf89215f41bd51294aaff14004a783e89eec6acd648afb645793dfcd5e4ab7a6406a7b06c85cf00533eaf77c6ddbff3d25aa4839cc5d6aa0f14a5925c0a32d9a445768524164e2da427ccf8e54b7a9bf52299e01b91c868cf5b4044b45ef5ca65da8d3ee799fa20549181f77be66bc9817005e53bd1e531e6dee9dd0f20ca8c3fca42ae8908872fb3427f975bf8bdbbc304d78748317f4b67bd3dc7de4fe9b1b157edff159e561ebb0f00f5b33cb3909a8a497869f51887c3db6a9b772f81fe2884ede9caa523046986bd5d116527e1ea70b1f80828a499e31debc9b76844336025ad1b21b3d2015c6b1a54f0545b2cb529e8afeda16aa48ce0d0ece560f74ef2f581c66a081600066eba0b1bf195e16462af77610d6a151195830bd0d055bca6d1849483129a18ea91d797dfc1e0f6d07a3436d18c0c5f334a5c4aadf2e5056d19d0159c715e2cd650319d32667e302d8a9fef3af6a9545e0849f21a07796e59057adc42627a0a5f8c5ea318a090a49792860132f0fd5d4919ee50f200416a5a2af10acc58971f811402abf9047c9fccbabc6f83492467fb15148eefbf12e03cf4d5dc73c8b11cdc329719b4f8c52eaaf5815a0e933a84288d3b82bd6d386fbc4aeb2bc54ae70d67171c67f54db3f36baefa981e124737f1ba0695c790535ad233b152fa5892175ef3e3f23f536a8d0a6652c20e2e82576798cdbfcd649236dfc11538f345b405005ac1a5ccf6b49759d8798973e0df4e77e0c89fd098e8419abfe2f5896067246380154e1c9194733a0642df08c2e1298030c2cda913afccfd1f25e5bc92921e6e5271b9d827c85fe64ec46d1d6b1b7c7652c3fb8aea513e179568c8b9fae1064208d306916576137f0074229737030a22f693a6e1e27ba3a2799240cf04f21a4906b79794c495cc5a0c3172f64240935363b2a03b8a498403055b57fdcbe60e976d7a65534759f378a8367c72ca4e53cba16fb2fd4c269be99f9c0b95766e199b0cd0ea8195a74b08a789ef979eaff7f5b1f2d2304b25fe75959e4535e2842c510fd683ce6a57e9f88241a6f5e1c83b344d1e48e8eef6207f4c695a7160e42d9da2b737bfac00c86673a90cf1af4207b91965c1452df1cdbce7fcec877fe50ffb6145770c0185228a56decdab132677f095f41faa588913a7dbf6d99b9ebc195574350eb6ffec25f1454cb04f10bac2497afa7c07b1fe972a0d9a5130df5200d03cd4e2d8dd918eba724e04844ae0ef024e4d0816c80701c32d846e69b3d1099ab8d3f7a5bdc46fd4ebcfcf083126cd4f29058da7f6527510a56b2728c5a305a5890ea3956c25e5e70be606eed43f110c13e4e5c62a295060104eed434c290ccd370073feb94b0cb270833420422239240910eb8bf68d9e64695bae4ae47b0faa67b705e5dc498a3de1fea3e10d1f180ff73a45e47adec3bbfe0e6f74fc1508adca94c2940a080ed3201011cc8600a4587a4d7b5c118248d435316f6892b60e1c4cc049fed472eb006cabe94d3227a597107ee6ae46ac2b0495da4e2312bd2c2665f1e8f3b3af04bd11268ea793221028ac9a53af4de4469bcb38e6b7805a1bd4534282283baf404766e3aabc57e27eaafe6e3d526ff63ae74ad59ccaba350bdbc329066524225e4c83df6d4976b67c75a0fe0bf26ecb8917a001ff03e5182a561b20265f192fcc1e8913e9b604dd3a8c50dc6de4dcb715aa8c5ed6b1dee520849e74cf1e25c665e7ac8623d5786b551d0e83fdecb7a2e048918f795f25d9245e7c2b4b08639891fa08744c96c8ae610c5e00bbd4c00fb8c78cd5a56a6d9d1c3b91a8aa49d2bc9aa7487ee2675248afd559f2bf2e53241be700c20330aa98139849bbd25fa918c0d00ee715387f388e01ddafa0d9f32f535026c2a1d5e5ca1c7786f70297f692765b5c2060e003779ec678f25b0a8ad3f09003868e31d4ca1490751969755664bf9099ba09af48676c203ef6d893819fe75f9f080a9010cb0641d4108e4a034e1066aea62380082e9462d5dbd9763295152115d3d906e0f81405b67c03040f1fdba9e8a22291cee66a213da00a1a82939ca5585889443c582467dc98c1ec7db2ad0cc5675d6e57fdd11b0d261f95c9c493127173e9abb79820d59d5bc621de7d92f73eb795acc475365f9ec3e00f62de73e8aa17b0d82a2b0704b5f64f67f0a5bdf5b78ac8e7b56323104b97abfa18b8f618b6ff8ac515d30dc7e24688b5bc661dece854e144c2e6844fa0f73151bec43c707b8a904c1ba401150cf476ac133608609a2deeee4be71914f8e2dfa87d9eddab673fcefb8dcc149e3bd4398c3e21fe4f9c94a3a95b0af114e67e4d3256f6ca5769f8d030057a03c8392bee6c04094e843903fdeba4cb6b1200dbbf22b1c057b17d1fc74ebf0e2b948c48cfba494b57cf41d63076d21d63461940d91ad5c7e8b0b9119bd9f070029c395ffb2aa0e6a42c50428ab7b9e4601f855e3b2f5a517b47aaf4530d4c3703d108a6c9ef64f6333c26a8743f82a9de11622f91ba4df5453d0d8232a12f1807cdfe00795b0fce05816ae6000db48ff5a3b377cfc808ae6a34a1b751a2396709e981d204e448bf2944cf4eec8b08c07fe3e7d36d29378fe0aa213d1846767c0cbeda7d1d59676a17858927d239317a93d6abcbf2d85c35bf6887d987f8b1dc1ba48554530c6c891ae05d490a57578b9dd98d2f5ab56ef27e49d60fac56abd727f7d7d9b9edefd4b80adc1d26d96a02055030ae5b8b56f5370ac99312cb9490248fde5d277d0de526e0e8e4502190cd6bb86addadf33ce872838dff1bf67e8322c55d8f29137c13c8028fd4712a8c59ef5357e978b01bfd5de3caa9372195d51392e91f4e30a060d554050d0f5f69017511a188c84eed0eb705862f714268381e81321f0c2fd174a4c84063444dfe47ea087231e50acfe22137c25ce4d6bda02598f4d95b3b644a74e80a2dd2a9cbdfdc60db1cb3d9e81699915e8986c24a992b9c3c317cc4e2323de8a35c71a5b48b39196ae83ebaadc7b89c2614c187c4122b0d2003927f576ddf71f89698fcc08c9164a02dec95f7850e592dcc22c0d5532dafff7ed0586e92406009bf7252044ae37de2de248a0920419ccc9306243bd20d02984910cc33998f22c46a75e769312f46335cbe0839b44dc97f3a8dbe4c9b1eef663adf180e97f4dc8193376f7638a96512f0162bdf052c6f70b68712a9c52394216753b6e40d27875f59202d6169a3be15181c6820fdc55f5520a21feb402ad28a19292df4f84673c3e470e5f4bace0c17074649c80d78a2fceab1837af4286e0556a9b68e99edfda0f1b4d816edb9a17133b306fbd713cc57e73d7fbe4626e8a5db2e9ecefb436349c7c60683cfd01d8c292be9e7be832a93466e72662b06345508f78a1c229cad1d62974be787f3b43446e56c0ec14c7c1916909c59681f510273651cbbfd259282295fe30800d0abc96432b550df4b2cba15971768190a089a294e6395f7db1923b045ba49a9288344abde983e246667136886d1d6451c2e590f932f74a6919e95b43a356b38233212e30eb1ffa92a972031fabaa5992ff19f5e7adcfa424b76e1794930378c2ec7d417b0ba06c0995497b4bfb5193909852443a975b6f2f1eb35f76414bed66cdbb4d78127d777085949554cc066be0a389061a2612de4e3aff95ab471fca7edd7c325e46b6392741d0a8a6384393e1d88ee0ab0217ca4cdd7a540ebc592450a7ccefb601b787b8bb37f87acce0dcf05aa8dab9248671e0c4ae48e68be16c28e9fa7892392c327d920e7f228b1818c0fb6ce357e2a6f432a73b1625270de7598cfad78fcd4ed8cd61e0caeaea73e90025ad2263a0491cad774433a6a2a5603552d2432c3562df1960448cfc5c77e9600a342e5a0fb59d2d3c59a5cad80e7e89a6cde76404a5192b764aa2a5440ca4f2f53b36d1f973eb8a42f76cb8b85a96edbf9ede2a7e45e873caee089870a64d30154122507aef82763f71c119738f8e630ba183c3dad4bd7dcb22e154915ebed2d3049ad556433a3d477113a0d1ec2183cb40ecf9c7b2205a9d2313b9d284f881eaa5a0df0c779a62ec623fa625564c908819871d0eda6ebb87dae27cce9b217108c253412257762231fa826d1ca59d856d2422a3b70ad8db8f835e43facaa7517f412592fd56b55c738e15626155265f5e08a30f9e2380ff939680fd31d5c9818147d43dedd9a75c130e01343ebfcef42f34d9d58a395666159665edf5517a715e11370386245aaf38e979a6de8d004b021644af89e3a8af6a9895e15ffa5a62a85a2ce71fb035b791c43e9fc861c3fc0378cc445354c2aabe5fdc8fd6abc7e2dde7e824d41360f1c664770896d164a66d81f1c2099b852ac1b6231abccfa271554713725102613e592527272ee3de12a518a648515ab7bcbec838cba30bac22645dca6372e6823a08deebe71d5ea98807e9b3ceab9bcb3e98d3d21fb7bc97314374f8736eef149f81d7e60d6d08c3a748759a1e1b25f0d7aa34af578bd8d1909e5ee4dc730b341d2fa5b0f1e1c62d5491d963ef95bf6b074f471cd67bb3be21f25316306a63927aee248ea0e760d9e8991b53ae4344fefc91c6931964a25e70b13c0df04d755b1031a1bb9cc7c0cb893f033119f60ed7916b09a4744a7c4932f9d724b44a7274973743bd5ce061cfe78aaa07e08d9f7a69e8acd260ecd091a1ec7bd6bc2c86edeedea3839063635ccfc602b537edf32df8a6812eca9599b082cf34280289889631b9472c574029ebf387bdb6cbbcbc867e90519cf30e47224167b9eb6869fe71cd56741bfa44aa926f0373e0e53ffbc68d27fe5aaa2f7a7531407a65fe05dcc42adbbc2e15a6afd7a3c78e2f101fb8bc68725bd41f079ee5e2a5d465e57653e462f4cc3ec94e2a007b90caf34b0677c9e56aba559648b740822261f54ac4a38a21b66b734917e2db39284bad9731e58dbb666de753ab11f8121b935c281a326d7fe09dc225c0b5c0b5b0c315ba36d6f9671d83f86c238edc12e2a8ec4ef750cb0d3d098e85595ba077f002771dcab930abbce17624af0fc9803bb78964b8f69e83af710afe5d6cc8f47845633e779372fd4c378ae0a07544714bd1fab35ef607011c6d0928c03ac61f8bb16ef1689c63e520dd1e0061aa08502f49c527327715326a820d485cd07887a0d6e8adcaa8c1c4fb19ca753f2b4911717e8bf9d6de58b0ebfff6e1cb1c119116acfe1139ae905d532195621e8c7811b27a3237feb01af93b31c594c294f44db4b13d52caf64b91b28c85bdf10e3972e3fd306b16506999526687bffcc8b26ebae98d5458b10a56eadeb0359dafc771d5c821d5148fa5058ffd8ad8879ab993239dfa206269faa645b647f130ae331016c801d4200a4177566cc3d3838c45109a407b55de78941ffae8fe6177b1d1210644651ae0a77c1c7ab833a81a97e2b30766308b80045d91a5d4437f6c1b0bf205f4a40802c211764dac2ff9932cb07239480b1c0db1f67befdc739d191fcc40edb870842d4f014faae3b25722c93da7b5356ef9f59d394706fd9b4848e6cdd1f6d981d02c1e5830ae59799d597b732447560c47d82bd2a41ce7c3f119b064d10edee9a05fa19bba76589ab0deaf8706b80a7286618a2c66fe78004183c0fa91651a96260d0d914f3f8f3c43c77163254d0a09b3fc95cc11f92350ad2e33f6c169d8029d882d113f3fa27bcf5d4edadc971370be191b313598bf2649c391016007fb5174ccd52d7012d39d1d861a731cc45b296ba1f6845c912e4211782a09a2e82d38f190f8ffffab2e164ceb7a39337411ba9f57f9ea55d6f215a47a897bc2501d65e640c578dea884cd14c5c854f530741f7e45673d9391a3e40ef11f5080cd5e73f44a0a318bc94065bb85f4bc8bc8220b5dada187ffcac245a5fe5fcd61c0c2688914c38700447fc88588de588e942e4b73a32966af7b994d407f3bf2f1dc0d164711f06e29c2cde0f865e99b56e28f1c6d9b3a8d5976edc08efee5cc029959b87f456b940aff559971f91334860bbd138282f42369a3b9b1a298298201ca74935b9233330ad150343dae999e788a0914e33cd5dbc2125239ced1e0f922e57d857cdc852fbea618373df51bd7eb85d605c0765a81d70e07ff4870fb4512fa199f4529c894809df36783c415b4102b3671c9bfec49b039f6ec3af71a0420c9d9d7da74284cf82394e1fc3cda21c5a6f0b769bd841fcd4ec5cfdd94d18cd1ca649c949279e93aa7985fb1752a6c0535cdcd5290523cfb3b1e1675b9098341052cf410383a5991c77cb7c4c329dd77da3da6df0a086c596134a966dd14f71234e7b68f13b28313ec6e69d74f41532308f6582b6dd4090db9eef2e26b177391d074f90db2eea4083f25148f5585168aa5941b6086e78d1c5d7425329465ac9072d1d51448700c369ffafe578e56577af31dab3eb3581b151fb0c81435e9e451328ba5869ffc8b023bad9485a18be42d9b36f455a2fc8929b98b036257f6e3205b75ffb76fd5e259e348d4ba64aa2862c132fa015c12fe25fc56cbf76b370d51ba172bd0d5f4ac5f2fc2c1e9e33e5281ea25504a3121a5a9055fc28f59044702a750776c47fc301e959e462d940aa019d5d3e6993572d41848668f6687f3c30076bedcdf79e500dd3d6e39fb8dfb771e0c1f61d11a83ffd13deac03879d282d1751f5771ab8236b6303e5ca5ae50e12eaf2c64e02ad5aabe15ff5bbf2f7d20017165e21a1b3c39e90633fd4e52ffd25dabb3c2359a6bcae94177d3b069fb6632c0ec01b6dbd3c695bdce702564f350412c6c7826d036befcc5b1fb5f39735022780f41ab74fca8eb93edac43ce9fcee8979fd9c3fe80894878a0a75c89f962a2583188a91999edb980c6dc1c0b1cddbbfb0dc2f54c1831b97e1f2d58687fe2b0dfd7ed13545140103e66d9ef7bf01ab4c8d3b184295992a34f43683c66f373e29154b5ebcc4f8affbb07b63eae67d86c7e2a58f4c64b6c3e0d5f6e2e2f962860eb40f1c56f6b2cebe7e9af8b9b3ca86c23c3a7bd479a1df867a2dae4b41088c0e02a0e0688ee555038909a12383e469c8b5c8794a23e11b473776ff83ca8ec029961ad53a2b243dbcf6093ec89a9619606791535381bb773aa59d76a0bc0275", + "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e974750780303f2ad158d360e938540cb9fc2ecbc5168b0078e536f2a2c847cc131d440ec0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000c6e378e01efa1cb216ab0ced340b44360000000000000000000000000000000097621038a18ea02190913636079ece971a28590517ffbdf7ff33ba52157bce68c37c073712ba7f38d05290ebf864b98223de00e9e865ef8e388eeef184b6f39f6bc05eb3d0c54021d2345bd1729316ef17122a4b371cc1b35e37beb851894eee43c8b48d01f6e641ad9ba6e94da997fb154d92131d18a3d5e38ef60661e890f7ed9c3c630efffcd8e51ee7278f13333e167714b66b4e3e97a46578eceefc9955b033a18e6f8ec8add6f96a917a74414421b654b589d9ba8107e6007b5a8b71a20fb1deff0cf22904da6dd4303e7342972486fb2c422fc657a57fb1fa3501d8a94e68d15348674a20401c431592ca2aaf2dff57e20c39913382bfeaafbe4d2daf8171ebe9a277a845723db5482f9128cd" + }, + "decryption_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000616da7fc6a6d8cf29000000000000000000000000000000000000000000000009587bbfcd4668d960000000000000000000000000000000000000000000000006121d7f725ab890ec000000000000000000000000000000000000000000000000000254c3d3438de000000000000000000000000000000000000000000000000da6daa0c6a5d1b5ee00000000000000000000000000000000000000000000000704ad42ddbc8965a70000000000000000000000000000000000000000000000044dae6888c0469bbc000000000000000000000000000000000000000000000000000038249694578500000000000000000000000000000000000000000000000cbe544e1f83d0969a00000000000000000000000000000000000000000000000f022e5672b0d807a7000000000000000000000000000000000000000000000007ea76b9db2039b97e0000000000000000000000000000000000000000000000000002b08a8ad0db8d00000000000000000000000000000000000000000000000d86835de0e0ee2dd80000000000000000000000000000000000000000000000069601171ff6c2bd9200000000000000000000000000000000000000000000000dc64e0a8399e2eb41000000000000000000000000000000000000000000000000000122eb6e0ea81d1d0f557691796b92e9da20b93e97a0863ab1fe2ee78478842a0c49f45fc99fad1db541b4e5adbd88c9ddba68378f955a0fb8fee6c21ddfc8e2fd73d6ff009a283005d6958a7b2064dcf37a906df83f6046a1c41199c4fcc7b1de51bc48831aa306e4801d58d108d333a100de61c3a7c902d11c47b1f3c7d3a0b25a6ad72132132f09bb7f0634f4b826b2ba79aed873c3f63f0bb72625a7d02b4ccc92408ae597051f526e4e6e94fee05e2ae2d8e4b12904bda66ebd9c9d51e9961cfced09cbe11d880ce4fb8dbfbd641d0c0636c70c7c15bf3e232af15eba8b04b2cd763212bf230710d91859e6bf1de118a016a84a1174041993b37a39e1bc004c1b8da7c3b82b019a2526f1f013dbb5954649960459a4c506bf44865e1d6806947274b51dbd0045c746ab31b273af3beb543fb20fc9cfff80cbe7db0a0b511d2f9b14502ff12f5766c56461c016ee71291019c84e22c333cd16bf76310569b0871fdfc9d31b1da62e4933273fec94c2a3cc680ba04e9b6b233a0d13147367ded65fcd59d53406e9772bf6e4ac857b26dc812bf34a1db26c1b64a233b0260626c2469164115910b03889019586c0037c32ea2286fbe1bb31259f82545e8627c3b9e841226d212afe130cf4a9b76961d989066bbbeb66a469dc0b056c285083d68c76bc2ee0f10f7a0f8441de08dad090e4f7d846c46c311aa23fd6f18f312c62c8b007bd61472a2fa97e9e5e5abbf3e1351c8275791fb7d97b289da247a09935c9d149beb26a1d562f06793f41cf7b646a4e457adf890d6f0bbfd0ac668e53ac69d0eea63b8329280e22aad07e3aed7cb334e20e466b04dcd0ce6085feb9a6e975f08da975cd1d816b4b170d94dabe92ba65bc039266a2814b2a91ff6d9cd59dc8488720894a04abd5ba314a630e81139ae7217116133f998269c10294813aa4775e0b16da5a0d23169f49db334305c3cbad59b33c9c08ca8fa4c5ddc084767a3a1005877fad196bbf853f6103f56544f009d9ae15132acb6b48466e73ae67d8c56770d2b4131997c4345fb6896ba5378db26aa654c77a7115a54cbabcf7acc4d40bfd53de0211274d7054ac3355db0cc69e8800a1e98400c4f70a8c9de3d8dbe179b2d44da025d39a26672fc01702344e9a0658c0b20ebe5a51f8508146f73ff6e1cdf6c2e81cfe68d354f0b72533115a2703b6cc5f3f8dbf779cacfacd8680218727155f0b04ea4cbb18eeb93963d0f419b0b5fa441d4a833c7ad7b0d24b0349478764c6f62fe88e483fb87415b1e327ea905d32ba4ba50397fa0c70425ca096d2311c8b0b154558e2f8ca674b7f71ca9de0c8a93088d9d70a28cbe908285aa453e8379f8d0157cca15c75f28dbfc29e918d2f3a7725b901fd5c757339442272b8e3598d912ed8c10828fc7349d1da35ebaacc78ba86ea2feb693b2de08575d8ec8a80515b2678034c4cb3e15920bc6461a3eec15d9f7c19db25ac3641a2af7839540cd2561645682683c9e16381f3c7eda13f44ff9d6f7ad00a8396d92aea2ea28dea93040360a0de2b389e3b93340a145e97c0ad1a45e8aba6843a4499fbf9a31ad6dcab0dd9a87ea40990c30fc82076461d9f27e407bc86df402f7b38797805c4f388ea242a50f5afc343005ec4fa3d15f37349d5201d60204b3948860efb12aca466de2e07da20d2bee1ff4b716cea16ea40c035b6434208394ef47618c9a05b36b21e04420cee3f04ed87c1f371c2454a90ea9fa4981de27084d981ee935b3956f92c2d9d41e8c45932a7ef1260ee236797594bd3f2617f863d45bbb60820882fdad924cd6ca6c8bad6af9027a28bf69d90ede735d5fe94327977bf453aeb327cb99c16b1f87895cc16b0a86d88b40959d9afa7d5dcafd282cadd2eda3b6828fdd574098fc2bf635fea1852b1d69346c936fcd4e7f0c3073514acfcd9c7330ee361e81c01c84df3070615eb2ac30aeca2f379dbc4f11d734bfeb6529aec25a20b26fd29bb71d9e56779e362feb0c4f8e27798321341a8e8f3d35e67acdc479c642fba17ba87677f815f1f8d15b79cef7e2533f16ec1626277ffa230fd678c3c9b12160bf78e02f78a2d70981a87a4e711c1422fdd536bef214655ab3385b4ac07f1d329cc67e97f5e1c25d04d7d26dfb744efac8ba4787e1f213c4a09145b0bb93b6a04070876e3c7f4e4f0dffcc247525f02d8114e0c8b5228a51726ea1a26ee0855239ec8f411387f77f5cba25a65d6872cbb34a7aee156f43c09b0b5b48fecd1a312c270ca2b8b5482fd832f9099c6438547f93cbf674271b26c23ef795ef582e81a36c5cf6a2564e10891b4ef9b29fce765bbd37c0fe30c95a81c1362399c4116280a46ff3973b0b348f98fa741e42fb67e0a4139da4306469e1bd02d119d28e116127b2be924e581533e1a290e3fa117c0f04d0738dbc69707286e5abfa8ddc405400d1f696a80b5aab65c2e70b13f6ec3ecd951208f975b6c80078d8e484c770a8f69c998648ea766a404486515637864a0663b029c14f33a2bb8a60e2894f02c5c82c192c6c2bf9f20eed8e920b778b3af947df672a8275836e7dfed4cbf662ce92080ab808e197dc14b88d00cb6319b4da02c9807078e38467a7e8d310d9207d13c61a05df7bf7766e5c4e5094f91f3d6078666ff1b7f6a02035143dafaa501d47fc044bfb43d453a18f286f09a28b99c7ea1ca9a3c035e7f2807e8431b660048afe711b39e0ab2714be4cc16e02bbff9485034d9c622be8cac586ac080ac1d76b5418f677e6a83252f7940af0a5daa3eafa21b4c7f812c8d2bb376862d2b1479d741f8dd2b08a6971f6e1a1583b315f6467763dc2167787ccdfdb40672900fe95ce635ba0d06e4ff638f6574dead7ea0290934e40e43e210026b650ca20d0e2d75171be19878f9882d0d1810cc8b6f25156033f014b18333518a1e57cff4245a086e4c21a79596f44871a4665222d28d62ff5b82f73dadab1f20344e2a301233df15fb850fca4f43366886b60dfb0c148e43f2c8e5505761054f031e45992e530a393a66137414996546d93ad7b66a0a86b52f547548eda7c5e9895524b511e5a1c458aaae8d6634de11003153f68f16206876b45e7188e948c3f1daa18525f9048ecbf0206c2f818b3cd6c8f8cbf4f3f3f53408ae779b0a870b51792546102234ba05de1caa383cff3c80772d58d6bf23c4ab08750bf84cffa0e9bf020f19d6a7e461d8404d52ed1bfbb6eb292abe3359d175170898a2480005f9db1abd0a8a310ce3514a6db37e25d075b0f1a0822d78ffeefd3be094301f2c1c6febba07df3bc9e983118eb146c90b068a7c634fba08a71f7e5972f225187a78420bf21a477c7e545db56ad031125723ecf029261e9167e4ced9243047061111c9d89002c6f0d4c6238de97e12c43b89611e498ca6f6d8e2c1d7f04f3424399f4094ab04f76c11ac9856f9164971c2ed828ca4ad43e26981c1132b1289800c2e5f0d5e1c30ca44cdd118597ee0f62df250d260a1eb744d707b82b118b985f1383d477e12acc2957c37c491c396614b2ad84c3969ec1ec4f1c7581c3712a103196948442a6907783e4ddc2a1462e6b13a13f9e217f4faf0361ce35af4ce7b349199ca6208be62a56edd8e4c45d7da43d9fc5acb3ff2c3a1e8ffce95fbafa4da21ead9210ad32d381443055f187a6cb02bb24c4c702af499b4de82124499262dbda32d5000057871f9cc917de7237618da0246598a125df8149b1e1dfaefb350506237b8280517a3cd38ba02d278db56960f92e1cda3d667cce290550ef00cef68cb311c28d981009c19e0c73a175e1f8bc43e7f43177c46940bfb9629b3dc8bc2865f5711618c23138e3ed27b59899f18792d4f410e9cee41597751d50144a0efb7722424247808d0c3c7605a628f75f7b33cee58e4a298f8db7e3556a42de5a519523e19edfbd5551c2412ec0d91b21065eb911c75f0c6580f76fe4ae919dd9e20d90e04ebc9b1779aa7db5c235c15d4d62bb0a614c2ba0ae188bb90c08929ac3425be266e6c0828ac369fe62ef6e37377a346655bb964fcd07085830f8e31f66c247e04086671961a732d6e9824f107aa9cb66132da736c241a93091ce4a5d0328775161a9a5cd0a7510adcda72d5380a8e4261b99479379f9ed014bb36aaf09886352d1750492991f7b968fe80a366b41edbe23fc91064a7b4f8279975369a805a2011d4255af3918a920c857fecb08ca970c3bbc183f7af6cd4ccfbcfae498429d219e4de0ff7b638d612d8a932c88edbdaf91dcda4fadc0dacfb8fd6d8d7a6d5f117feb2e63aa7b37005690a183b7cedd096681e4f472b36b0371e87462f5ad693052dea498ff2ddcbd3bfa0fe4e996a06b9790c26f6870dfc3e18c88981d8f8c214fbbb4970ed74213912e132feedfd355ba5273b00d34bbfa521e5126ee83ac50c6018d032e9b1316bf07d5f6961424bb63041a2c28c55f1c913b15b637204b41fc084c1b1a31602652d016d9178eda06ee7128c09bd600a10d18fd526163f0b081047676d393073b466e12d85711f61038a03e57ab4882971ac99b0d6b2bc0e1b9f299e58527f0f16aaf6871e1db4ee3c5dcf014c2dc65524fb5ea32001e92b2956a53aac0a6512d222970ba9e533b4ef412f10c7eb6d34924f6793bc34685402cdebfa8dfc72a3322f3dc298df0470a4a14b65a2cbd0cde85b0b03915631702e5cf7319251934efdca0c802137e7622627764a0f62e35ddd4fc29ef4624f36221330a033a4d4e45bd32d0e7515516aafac2543bceaf4d88c822a2807c777662cad17ca5faf0c64b97712ae199859317feb0b7a23b9e486edeb09299f8ec8a22d1fd21e145147c31830b3d2a93407687ad92abd6b383db919cd235516e7b45214e830bf52047cffaa97e4fdbffd6cf7cf874d4e71d9ccb92d11867be2577f4d1b61347937a154f0694e2db859286dee84b79269d8b9916562b17ee7e0b103f7068e0ea2e5d9262d13e0e915ceae4c80138a00a157779751498bb2f8f0cff30015fd049acd6daa9fd01a052506bcbd467d78f415b6b30de2ae22f9927017a0c22917f82f9b9c05bf6547479aa55ae683aa7457eb8bfeda7a07e913307f4760440135b956996aded6319d9aa9abe0f76ed72488a3e22473ef0cde7c3736429108095e21cc0b47f3bbc9a0a1da00244efe410bdb1bd733792978b3e361144c3ec927c7cfe7b3fbc8c17738558bc2c90ce2ae06cd4ca92ec2f2ec62a5421a5148e62085eefb1a3092518429b7aeaaa7866b65ec7b74cf4e71696633f53f3ff622592a632e91d5af2332983ae86a98559f65d8f82edd814b7fdc257a8502ba0909071d670f16a22cc60642c30fb9fb7fc98474649efdd11acef790c632579810fbb80f8759feceb7061413e6df2f73cb040cdc5381d4b538866d384c44fae8b3ab330ff0b21753498352e5546550f9b114baea0ba201a86b12ad1d15d04ac3b3057507f7640a2cdb667262b3474a10ef58cf41d2ab58694bb562a2cd8feeab13fcdc1ae46053432cb7c5a671455ef7d8f7d8854fd13f9aad973eec42d9a27d2573162102b926891d0b8b81ff1ec89388965457c077b5e77106d056476a8d69cc035a2a6236cef525b4202ec9b49a7a24d33243c0fd4a57372970a46f7bbb685c8f2b2f02c81eedc45a4ba4ecb5a1601d9266daa5b594dd00dd8c5ac2c98bc6d1397001ccaca031b909879d6f36b49dce0e71c0a1f552d908dd7e18c37b2665cff1bb17fc40b5c7c46fe46827effb3dea634f7493abcaa3f21f974122b393ee3f97340a901c45b90fff46f3ee3a2b88256048094e6fc5d49b8ec566413d651b4bfd481f8bea6c3f158d26f2814a0a21165f5ce25ad588073f7747a302d40898305e5d0fbb41e6583ab90ccf4ade432ec1cf063ed319eeb2b6586abb670a4e8ab4f5d3207aaeb87540df8e47a12e79a8e73b6e9743aae8d5e55aae5da5fc551bdc8c5f150b1b55695d5420881d1aea8e1752398bec30da81de0b0601121045cd1dda9f19f7b5d2266d18c3df2b61d1b471737c2add361519680c8d0c3c151baa9a9c5619cd24214810f337d33af7a5438cb1cb419f4c2e4d5a0827a0e9cf91ff2947c6000ce6c8b213350402828339d996c44c2e7ac96b0452a829022fac28bf9c513e1701296727556febb60ef2acb478eb6d9a0e1eba99466e98aae13867404d352107d66c4708e57fafdc69de80c7817ae046f41cfb2882d3babbf5139a9ae4faa80bebdd2ad2e68bec5fcbbff945c5e7fdfe97bbc3093ba2b1f10c60af59cc60d80e5b6a0eb2cebee9e3537a4eb9f002c108a26738f9bde875b7749a07bac29cf527959a05a688c56c9d390a5dd346626ba04a0363646e6967033b9be18399be09288a4cb2e5a0fa4d656881bba6ed66180b6f4c0cede4ef98e34c5b0857bbc95b2f58bb87ee1c1fd91b5f887521033bc3155654bf8ac1a8c05cebc1f8be7e3a6917485cd6e95d67dc1c766117a3180fbbddaa099949c5bf1151c1cbefa10857b62e9d1116130f8343f772e091058db76e77567417464b0ff93d4585abed3886971f259252fa965f51868653047aee30d5e15c10cec90b2b99109910042dd655cb221cd2533b22397a15f8a4476ae6fd822077f56d3802f01d0b00aecca818ddca1f43544eb3cdf138b816ff5895940945597a7655e30b80c98616ef432c9da35323c7927924b4c1046711099315ec5d58169732b4b75b0a28bc279de6465a42b90ccf643157c4ba6eb5bcdcc7995ca022f0fe91c0eaf1fa910a775e32c70877ce29e5bde84a1189da233e3ace11f96364467be583eef1bfe52b1649a05a8bb44303db325b8fab5908df7878a508c6154937ffeea18cbf43fbf14200106941b8f42de76932b23e689995af9e3d04c0b37765389d1dbd1ad2fd720fb20bc864c025182b41e019aace2abcb7567da8a2449e1887286aeceaccdebf9dac751432f38e2e4ccdca3ac87ee7fad352d7ff41cf060d830a5568773f292d983ed3ea9c87b721b01df39b09be61d87dfac5dd5aee60dcdcfc8bb862c758500b189a324db3262f51d10e38f6d42a7fd9f051204231a06595de9ce681e9966313df7734363ffe1852b06dd157c08d974d7191399967c251acaea70c1376924e9082dd3d6eada315522ab87463acd7faba38e7e2f45677c6d6e2f93d64cd3ea57f855228b29086168d443b8768761cd92dce3ec5e7eb49ce0206c45b0d6c5633a8f7cfe5239b1d2ef1e141cc5a4c73ebd5827d91ea4feae63329ab93aaf49f8621647c7658eeac053de71515e765e1bf41d9e57fd7688cb8978b70edd88cc5f397c16e8bdae86806e7f6a5181704db97138fd3ad30bfe8f3f86cbb665f4a7de7916007e45137ad2edd56a1985f9a10a3df3458a734a9ecae9b3592e43bac49201782855b2155523019488b64b546a7aafc563a9b59f929ee0cf71f5faeb4ba027809a47ea91bc61156c1995627c4998f6ff7e8f59e552aac13a31d70e69b88bb179167f0ccad681dd077952dd05dd90a5cf24f2e5bc1e1915a996366fbd7e356358e0b92108bcc2fa60960482e8b422993ce58bfb7cd058f8e714f1e4d777dd4df6d94d185bb590e76ec3c2c47001f0726c38f0e9d2063f3ef67f0fb63023bd6617a9f53d4ebef1e0280a270fe757aac0af79058d909b8ae0361301fbf4252ec5b19dfcb88c9fb2e453ff90a2a56614dece9e3c3c8872a5116a230c48c7f987f257cda6cb8190c2d11786ae6a859b224093cfc916d924d1dffd0c72c60f04a7a4b525b9de3c0ad217f975e26a887400711a9ad9f1026afadfd263d3bf89dfed2e10939f0b3750912ca93f9498d6ef6f01d557fdb07a8e1bacb8f971f56788cc049b36ebc9944bc164843ed2f857d5b80c2d0e8a54340d6dc5ea6bd33dbf0197ac869cba99390d72ef15e01f0a8bcf394dae09bea03d0b6275f95009008923effce8d6c3fd71d7d05169b021ab02551c8061118dfe49ca2b249b80e1b31b35da1e0bb2986d8fde91e7e932c864120afc164374863379619a5f7f42a733faffb78e3c0307955c30f16aaa12541ee9edbdf1bbada45853c95bb84b6ae8b55727fecfd211aa981feb3195902cdf9c47b466eed7df5c99ed5c1a9986be4bff1f8ba0ca9028dca194a571ccdf56160d35f7db7b9b546457a3f58ee88f804b0af4c9d0ac2197afe5cdca827db53677c6c22cb123f66ed7c9a3a5d66631b4e2d28ec1ac2944dd8928c17b600462658fffb922dd59de1b5c4e3c596b135e5b3ec648c55487c4a4e9d03d0fb0eb1aaf689e5664411d678754c62a9c454cf433246204e7f0d35023465870ee1083f29e501695957c73780f0a97b7fedbc2a9d8659ee308637eeeef72467516b07db68fb78a9a9cd0d7e80ec1855e2370dc2a3fc5da2c00a6f2178d5ef6791a32ca0f2ab588997a20dc02b565f20a93ce84018b4f5e9e91b64f1b8fbb4451072010a3a6d25e1e1269b12967a77469f708063e9bb522a5f044414337ce58745a61bfbc7d14ec22abb3b904e2e7ef401b391fe764815648a73250f96d53b6291d924bd03aa9a5f58d5e893c14850fa85f01e939049ac3a56ace1b73fe06cf1bf4d1ae95413410c335e7dd27b9d0ae5db94283f75ff30557ee6ee8c2facd4fd1cd130640da57669309ad2ee076dcb7b4c485d0a68146b2a687a92c2645fde606d7e29fcd5920546f402de4ab466d0ee17e4dc11a167a17ecb77d8d34b4a67c18e1509ddf08b7a929b2aa92918a0032fa4dbcd8a735a34d11034f82e6f0c111a36490c0d9e41bb0b47ec8bbee1492876a765913a332f93bcc3dab8210c067becc09a0ad7426250cb94602452a54e1c073e2ca216a5f107ce4cfbb6b355d09bd65957285448bd1289902f5e9570e7aa2227117d4db540b9c97d3ed0f759545a81e4d1011380c194c5ea5db9507c00f61b87d20d1220fe4985e2e1d30065a23bd3156f23347b8df7af3454b8739d379debb9a5ab71dc50af6f215ec2a15792ba0450250004b392546927bd58a85055a05eadb613806d86914b6d62bffc79eccb75a7540a5b8d2e55bb8014d6618e71455a8f7b2f7f68dd008ba1cc25d355b90bc3ba1429fb08d96be9b5c21c7999f266cd03b43dd942ea09c55fb5bc11c6c5a174425c2a14c41fabff96d751937819c1ba20020918f9b381431322ef154b7363dd8ba90e7fec6e1072b896789f0f4822c0c147367c3fe82792879a0ab3caa19873431814955df92af2d824e8f1c3e879df0cc0d8a0f5bd76189cf2acc89a777aba170407d7788ecbb0c719fe9a150ad45a5dcc5d8d0a6923b7b27ef6c6b7d61c8d36551f74a4356e4dd1ebcef55bc96b0f6d4ec261619111bbbf7d01cfd4886d1de53623b33764960fed4f0310cf5bc7c27fbd4cc043bcb52e3e5a5855388cccac4c3e2326086873a0072e2bd03e12c78207d91b323343a2be00ca1d316015e6b7b9ae16dc2e72f35764059963da57573115adac29eaae415033e5500b11b392df0090240eedc5326ba3c4de726767dc7cbb6ce92f8cae01a136f39e72c82a7f455def1235497e0d5e3950864202a2f45edf6b800dc64b74b696f2aba673b08e4a2d822a929abc9e6ec8f99049f780d6281f48c9e92978df5d80724eb41681b3d4d2551149adab600d300405f0d78e43bd382f5011ce30554f420273618ed4474aeeba226d1c60827a4975c016ce132ac2bff671715085fa050d9512bc678279dabc7e2937ae6846d97736f3c49eb111407b8a9e9b127e5e2447d4901fc973a421ab3d22eb9ea67ff3094ae76e2adf6da4a0384008de2fd1fc52336fae0c0eb8a1f44109530dec9ba7bf4d1b3d51afae1f5d20392178cdb6f4122e1d9241d3d5b2630d22170047f6dfcf200ad991bccb04299c0400ae830abd161c6f16fd7f5ad7d55e2a91a91c308686453239d3a527c4b1761dfdcbee18db8f02e8efacf3628e99f32ee6e7244763d56e037832853423134a5f8a5641ae4f979e50ca4865ef06643f02fabe75f0cace0c0b039d4452ae0f365ff529891fa3f371e664b389ef7886eb3030b411db22fc59dfab6170e4a1ec4e136124a10896528963dfc5c96ed4259d1332ebc2b184c375fb6481b068abfa1c8d7b863b3c764d18dec95f55a13b807a1ddca78ef66c3f679f534b30384f7a796467db5f4e8595e0500d634744ca3ede0313c61ecc7c3c379c7edd4a9e711aac6dbb1768dd7604ad4ede42a037cda84d13a90845592175da3e3c72f9eff83911da8d0f3279fdb75c5057a140ad1feba217ff17582509379007b855924ad126cea015f23da97b9ff1a85d2e26178e7f492e38990677a0396678b98a389e5050c5a74ce4a769d0f445746bbff937f7c2032cfc6627c5c3af996ef37c2bc10c5a7c94c2c6ce88e04e466b9c01bcdaab60491533b97ef4a264153289662cee698be8ef26b021adcff3be3655a6de1f89d2271992a5af8f646b8d1d1887b122539bf5ff80b0d73282d288a0b7b1f6b15b7177151794da1af79b8a3265adbe34932c3a5ddc7cc991ae4280d005ed69ee78a1f612d55895627f1bbc0425d4f27aea7f2030c68852f2e2a869abc8c656282a69d91f4ce47b4ac4253caf925dd52ff54d1cc443761800deab6659b954208a22c27c09d9bc509923b5af45933add7cd707235c987f02bc0f609b1ca5a831eb61ae990d69687bbcbd8889eeab15fba07d77666bac222248c9a11092cfa1368f7ad93e2c70a325f6f55bfb9c2714850e968c5f9159bbdc4da5b5845ab1f4b196b06b860c8eca51ef7ee7ea1c5ac6741c3b45eeddfa510a3b35c87046b84859d192a7482e3c7825d060b1598765bead1f4993717136208fa56b48f2ce52669d7c2aff3d0bbd8cf1549ca6332ec3df67c16d599ebeba774d36a3c23e47fb3893ba793a9a2825bf3dcd7dfde5259eabdc59b130726508bf5285e8e462de85cc1fce521dd40ba15d9af05a686ca5bc521d4f87492904e9471a27df178a9c32b4cd292debb3083e36159fbd062777ce3a5cf7e42b68cfe357293dccb10a66ad7605de413bb62b339778547ae76ac7a2e1ed9831ef465da55fa64c661288a01252ecd7b9f5a62e5b8687ec4064245e7c14f1b044bfb5833f5f5354a5e52a118f30523e80fef11c4a642b47eabd6c27c18e8834c0aa44ee3353aeca611d41d5a139741f106ea329f5a4ca9b810877f6a0530f157229cbd931de9be6ab060c9c7aedf12a6d31bd1416ac65e49f6fc1ba4fcc40934eed1ed6750feab533cee8d2a5bf77b70860230d4bcc80e332fd6dc8eaa597a64910504bb9f7a5951e0b1cc5549b084e62d3612f42240d9fe81c6c474f9c91184edd5d3d9b5b9211e72d85f6d4307f89b015d01987a2ba529b3e3de4305c8d91a646ccc82a4bedb2a4907e9159c7c99ad0ff59036d6623b151c78f80b23febf74f81d0a92495ef3b64f19e66ed2f41012533980476096427924dc67d52afd687936db302e8bd625ceac02ee8d96da3ffaaf18d0dba69b142c534b2af9d4c54d09bd072b0abf5dd514fc97ba8887ba0a3cd11601669661d81e87b4a811cb36b178dd20b80a0f2a7d30e3cf1be317eca455d578327e0b8517954ae519d46c43b511ff7b5b5faec58ba349aaf5e9869f04c2c67a30474deb6a73b2ebd5ac1134811f1a36c4b25196a574cc0466bae6b3fcdfa67a40beb8730f67992873081dd969bcb5fc718be38b7f2d905e0a956005ef56acc7229696bbf0b5783493ae613da7d540be2d78baa3c0ae8ef5dad872e5ddde1b4801ab09d3a847d4e7b763d045842143da7dd7506cfea1768cf8f6c02498045a5c12e7e1694b77217b83e4398daa91eede9dfd0e756b5454f4a53c44db1ed78b05f180801c4a0e6ecd9dd3c75364074acb511c8c2388bda38e4082a2f52d456d690044ac311feb298ab6d7975886247df78d1f764d91992ed87babb126fc4260470063cc176948b6830efede55d9dd95863e7000b0d6449779f26cc2fe918cbf16b0b156dc79e15d687a3ceb7f9e89f387efa6739bc8694a0663c4ddd1034e732d82d98654f4ab727422694f967ff95c25cdcfb0a7b67a9ddbdb38b02ff739702532bf9776901d83b68bcd265e6b441044b18a466db1744e60f715828741c53b38c2234f0062231eb92cbd23949615f9564eeca7e3d36a66eb8379ef32d66fadfa4142727380dc5ad292c52c7a073b76f8cd789835175390cd2e01c49670d4d66062ff44e2d186ace9c739f41b6027976d14bfc760f07d82c033b950d9239f066c31719f4e87afcdaf9ee8d0278b99a0e3d18849003d3be00c682b37ccc6095110605f89adb2b7f013fce5b354fb68b2b32577fbd6c48a89bc1217c32ba2023b975219cee35356409db3827f407d60777d58e8348a4b165a09faa7e89660e811f2b093603ded70cd3e08e680830d676175ecd7efe2b0425ec8d5d7927f40d04ad5902b02397c2b75482dafe445739acb0e8ac4e53158ebd6e15185513efe9d06f38251951521b1b37ee05c9ecf7335e1d561617b68823ee6ff207eecb139bfaf3c919fb94c51769dd2fcc89cceb51856e10d9739407ced8a9849efdc2411496ccd72ba430028754d7f3e1d7aa46a8a78e0a0b05709edb0612241c0431cb38d32a8c05fba833cbbe1077cbb83aced24898c91227ae2ebb7c185a8d7be7353f8a0cfa2b1e7248dc2974a6e373f4c716681a6f5067fe446777c3d31571ef38e09783501336d31654c7c5d95a34d5671e1079566c33d4f32cc5195d4a999f56be81b1c9041c23e40a59a05ba40da1f97eaa2f83d737c4ee7767a7f75d44f94b08c838cb26c9c73e59f573dd389732d09fd9fe9eae468c6b879ae3d2a9d2afc712a9330a21f7670683303dee05ccfc96beb8f2d894df04693179e2973cef115c09354775010f3c5156faba3e58bbf7bd54bee5e5308a25e94fe3b1147078b6147c753db4288927e082ab365f6cd4f74d0e850a3afcf01629f899df35a216e1d3adfcbecc0b5176ab93d7221145c6d3f2011852b1b0d5e52595b97c6d75e6551b6c018464068a4250a21e45c570aef973ff978e2d0c3d1f32ca88295e8dad9a6b665faa3700a8ff8659e594a167530a95f3f4327d7210e6b5963fc3a3444d16cf05a888dc17cd53ef517b3ca9f2c4da57eea412b405d27341a8f3da610420f8cff8fabcc22ea7167258ec2e0e0153836c5e3fcf168f25aa7172528b7a7a10e6a961520d320657abd3feb900a60de5d920a78a84b6b91bd86a032714f0729541468bc628b42fb2580aa35a66684f00e8aa78ccea16a6e2c2a76962a2c97d2bfdc28c1a14b1029c7b68587d6cec18c23893dfb394fa183bfe597d4d9f41afc074c0eff30ee90ac83636244fc935d52d9f8c32884d8ff049e073a3dbfe2e7f3a9fabe056db851b8a0ab427bd70186dac196be85cdd9cf6a472db35588775a41d8f5bcf95cb1d2a0a34567b854f3d22327387c2b32d30ab6abce341820cb40065f24e9b4bf7a111bc7a424546ad33f509901289798a8b7978dfb8db35dce1f6b3dba62a1c5ecd1b0b6ebca55130fff8bf52b3f525c4b8b490664fccc47262ff3df8b6042652960484ce932170e3593575f723508e98ee25bfd80c7c9d432d952dcd2eca28ab9b2294cc39122d4d6c0eb32e53e70a7e8036c79c0c0dbd3799259c82cfc1c7d0910d399780e2969a47f912e8b716239eede7888a4f541bb71f30133851e10cafa40d3e8226743bae7711324bc4ec129c85002d3083a2af215f38359d37b8d6ece90f6c000b0d56175158febef5a203c3fbf701312bf3d11667ba4c0409642de183162b2a7fd33f67c8f2c4df85c2749615563b0a782dd38927c24f0adb3dcd67a82c8d98745e75c91c8cb14a48a18cdc21c337d33defe7a34ee52d58f2ecb0f33e1b2f7ace0a364d7b999319eac3e703147b0f0289d13b496a5baf1132e2d69fea1c05498e6412f68f8157fd373a87e35aa4e60aed1ca9035054dc4624cdc293e6106ab5b7636b1eb293919de2f9cfa94f8106140a2fede50519fec44ee3b4d12a01f74160bfbe701af451915e10e836bf9dada21bcde42f5c5f62adcb5ddf571b2ed223c42e6671814eb85aabd89968c5d0fd5cd71293c45f665190f6dc4e71161bdb143eae947ac64357958b27ebfd7b7d747c635220135d07e5dff8c97c02671cc1498962fd55a7444e856319b117c443a4ede613035f4771414a54e3a189b7030d05c4fed5241d83bcbb4bfde04683b10d845642ea853252e0588d953592541017b5276b6057e49402142dff4a01b46e0577963f1d46395fdf2d11092f0ffe15681c8e2c65ed60de0c5a4fb3be87a9ae7795bbfe16c96f2a2fe963c12e7eea0f851773e33fd8eba81fcbd8a3f51081726eabd522109b504e688c1367f7a831019103d297cdd5e7d383882d43a1e392dde72d8b45f74206cb3a4af9dd534e881a1c9c5304e1951778074af2513419fcda57d4002f8ee3fa959e69394fe07757026d016f186a85c5dcf4736885598c99220b8e32c16c41fa25e9757c796abf710612a3fdac0fd88526f8fffc53774f41d7d5b985ed1af86b398aed2074aad96d09cd2356845017140ee303a82f21315d018a149cb23f4098c5096334152791f128495dec89751238d8f03030911d3d2975e8c637bc65a3c308ee81ee28022f001befbd0c957929a6cf5e6682b645d001a3ff9a5a118325a69b543d179f878b2d0c7808b8477eff26b1c176fd463289dddecf4765b048bd6cb82468bd00c5fb42", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e44107940cf3c3a18e9c2b8af0fc91a18c91a709c00a8093bbe606a2e9fd472457bc00000000000000000000000000000000c6e378e01efa1cb216ab0ced340b44360000000000000000000000000000000097621038a18ea02190913636079ece970b3f37898c93078120c11921f007446ceef9867e6da4e341fb3bf8991edcbd630000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000223de00e9e865ef8e388eeef184b6f39f6bc05eb3d0c54021d2345bd1729316ef17122a4b371cc1b35e37beb851894eee43c8b48d01f6e641ad9ba6e94da997fb167714b66b4e3e97a46578eceefc9955b033a18e6f8ec8add6f96a917a74414421b654b589d9ba8107e6007b5a8b71a20fb1deff0cf22904da6dd4303e734297000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } +} diff --git a/circuits/benchmarks/results_secure_agg/report.md b/circuits/benchmarks/results_secure_agg/report.md new file mode 100644 index 0000000000..c9fac38a1b --- /dev/null +++ b/circuits/benchmarks/results_secure_agg/report.md @@ -0,0 +1,215 @@ +# Enclave ZK Circuit Benchmarks + +**Generated:** 2026-05-23 16:20:50 UTC + +**Git Branch:** `feat/1549` +**Git Commit:** `604ef9af71651ffae19546146b78a7940744741f` + +**Committee Size:** `H=3`, `N=3`, `T=1` + +## Run configuration + +Settings for this benchmark run (integration test + Nargo circuit benches on the same host). + +### Integration test (`test_trbfv_actor`) + +| Setting | Value | +| ----------------------------------------------------- | -------------------------------------------- | +| Benchmark mode | `secure` | +| BFV preset (artifacts) | `secure-8192` | +| BFV preset (enum) | `SecureThreshold8192` | +| λ (smudging / error) | 60 | +| Nodes spawned (builder) | 20 | +| Network model | `in_process_bus` | +| Testmode harness | true | +| `proof_aggregation_enabled` | true | +| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | +| Rayon worker threads | 13 | +| CPU cores (host) | 14 | +| `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | +| Verbose logging (`run_benchmarks.sh --verbose`) | true | + +### Hardware & software (Nargo / Barretenberg host) + +| | | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **CPU** | Apple M4 Pro | +| **CPU cores** | 14 | +| **RAM** | 48.00 GB | +| **OS** | Darwin | +| **Architecture** | arm64 | +| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | +| **Barretenberg** | 3.0.0-nightly.20260102 | + +--- + +## Audit status + +On-chain verify gas: **complete** (CRISP Π_user + Enclave Π_DKG / Π_dec replay). + +--- + +## Measurement methodology + +| Metric kind | Source | Meaning | Do **not** use for | +| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | +| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | +| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | +| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | + +**Harness limits (integration):** all ciphernodes share one process and bus +(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare +runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, +commit, and hardware. + +--- + +## Protocol Summary + +### Circuit Benchmarks (isolated Nargo + Barretenberg) + +Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). + +| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | +| -------------------- | ----------- | --------- | ----------- | ---------- | +| C0 | 287764 | 1.46 | 26.25 | 15.88 | +| C1 | 2432074 | 9.41 | 26.79 | 15.88 | +| C2a | 1446348 | 5.33 | 26.99 | 15.88 | +| C2b | 2889001 | 10.00 | 27.27 | 15.88 | +| C3a | 3563512 | 11.07 | 27.07 | 15.88 | +| C3b | 3563512 | 11.07 | 27.07 | 15.88 | +| C4a | 1961956 | 5.96 | 26.32 | 15.88 | +| C4b | 1961956 | 5.96 | 26.32 | 15.88 | +| C5 | 3719555 | 10.99 | 26.81 | 15.88 | +| user_data_encryption | 1678200 | 5.81 | 26.25 | 15.88 | +| C6 | 3001847 | 10.43 | 27.26 | 15.88 | +| C7 | 109424 | 0.51 | 26.42 | 15.88 | + +### Artifacts + +| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | +| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | +| Π_DKG | 10.69 KB | 0.47 KB | 3125282 | 176136 | 3301418 | +| Π_user | 15.88 KB | 0.12 KB | 2973001 | 193312 | 3166313 | +| Π_dec | 10.69 KB | 3.47 KB | 3641070 | 187344 | 3828414 | + +### Role / Phase / Activity + +| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | +| --------------- | ----- | ----------------------------------------- | -------------- | -------- | ---------- | --------- | +| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 616.80 s | 127.00 KB | 128.56 KB | +| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 160.99 s | 10.69 KB | 11.16 KB | +| User | P3 | per user input | isolated_nargo | 11.18 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 10.43 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 186.97 s | 10.69 KB | 14.16 KB | +| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 50.52 s | 10.69 KB | 14.16 KB | + +_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **43.96 s** — not +comparable to P2 wall_clock row above._ + +## Integration test (`test_trbfv_actor`) + +### End-to-end phase timings (integration test) + +| Phase | Metric | Duration (s) | +| ------------------------------------------------------------------ | ------------ | ------------ | +| Starting trbfv actor test | `wall_clock` | 0.00 | +| Setup completed | `wall_clock` | 2.79 | +| Committee Setup Completed | `wall_clock` | 20.37 | +| Committee Finalization Complete | `wall_clock` | 0.00 | +| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 160.99 | +| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 616.80 | +| E3Request -> PublicKeyAggregated | `wall_clock` | 617.32 | +| Application CT Gen | `wall_clock` | 0.35 | +| Running FHE Application | `wall_clock` | 0.00 | +| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 50.52 | +| Ciphertext published -> PlaintextAggregated | `wall_clock` | 186.97 | +| Entire Test | `wall_clock` | 827.80 | + +### Multithread job timings (`tracked_job_wall`) + +| Name | Avg (s) | Runs | Total (s) | +| ----------------------------- | ------- | ---- | --------- | +| CalculateDecryptionKey | 0.04 | 3 | 0.12 | +| CalculateDecryptionShare | 0.16 | 3 | 0.47 | +| CalculateThresholdDecryption | 0.23 | 1 | 0.23 | +| GenEsiSss | 0.08 | 3 | 0.23 | +| GenPkShareAndSkSss | 0.10 | 3 | 0.31 | +| NodeDkgFold/c2ab_fold | 7.16 | 3 | 21.48 | +| NodeDkgFold/c3a_fold | 57.61 | 3 | 172.82 | +| NodeDkgFold/c3ab_fold | 6.65 | 3 | 19.94 | +| NodeDkgFold/c3b_fold | 50.04 | 3 | 150.12 | +| NodeDkgFold/c4ab_fold | 8.45 | 3 | 25.35 | +| NodeDkgFold/node_fold | 14.89 | 3 | 44.67 | +| ZkDecryptedSharesAggregation | 2.91 | 1 | 2.91 | +| ZkDecryptionAggregation | 47.43 | 1 | 47.43 | +| ZkDkgAggregation | 19.53 | 1 | 19.53 | +| ZkDkgShareDecryption | 21.92 | 6 | 131.52 | +| ZkNodeDkgFold | 144.80 | 3 | 434.40 | +| ZkPkAggregation | 24.44 | 1 | 24.44 | +| ZkPkBfv | 3.56 | 3 | 10.68 | +| ZkPkGeneration | 55.11 | 3 | 165.34 | +| ZkShareComputation | 38.70 | 6 | 232.21 | +| ZkShareEncryption | 121.54 | 36 | 4375.30 | +| ZkThresholdShareDecryption | 99.20 | 3 | 297.59 | +| ZkVerifyShareDecryptionProofs | 0.13 | 3 | 0.38 | +| ZkVerifyShareProofs | 0.31 | 5 | 1.56 | + +Sum of tracked job wall time: **6179.03 s** — **not** end-to-end latency (jobs run in parallel up to +`BENCHMARK_MULTITHREAD_JOBS`). + +### NodeDkgFold sub-steps (`tracked_job_wall`, per fold prove) + +| Step | Avg (s) | Runs | Total (s) | +| --------- | ------- | ---- | --------- | +| c2ab_fold | 7.16 | 3 | 21.48 | +| c3a_fold | 57.61 | 3 | 172.82 | +| c3ab_fold | 6.65 | 3 | 19.94 | +| c3b_fold | 50.04 | 3 | 150.12 | +| c4ab_fold | 8.45 | 3 | 25.35 | +| node_fold | 14.89 | 3 | 44.67 | + +### Aggregation jobs (`tracked_job_wall`) + +| Operation | Avg (s) | Runs | Total (s) | +| ---------------------------- | ------- | ---- | --------- | +| ZkDecryptedSharesAggregation | 2.91 | 1 | 2.91 | +| ZkDecryptionAggregation | 47.43 | 1 | 47.43 | +| ZkDkgAggregation | 19.53 | 1 | 19.53 | +| ZkNodeDkgFold | 144.80 | 3 | 434.40 | +| ZkPkAggregation | 24.44 | 1 | 24.44 | + +Sum of aggregation job tracked time: **528.70 s** (parallel CPU work; not P1/P2 wall clock). + +### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas) + +| Artifact | Proof (bytes) | Public inputs (bytes) | +| --------------------- | ------------- | --------------------- | +| dkg_aggregator | 10944 | 480 | +| decryption_aggregator | 10944 | 3552 | + +## Raw circuit benchmark JSON (Nargo) + +Source files for the **Circuit Benchmarks** table. Persist this directory with +`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without +re-running the integration test. + +| File | +| ----------------------------------------------------- | +| `config_default.json` | +| `dkg_e_sm_share_computation_default.json` | +| `dkg_pk_default.json` | +| `dkg_share_decryption_default.json` | +| `dkg_share_encryption_default.json` | +| `dkg_sk_share_computation_default.json` | +| `threshold_decrypted_shares_aggregation_default.json` | +| `threshold_pk_aggregation_default.json` | +| `threshold_pk_generation_default.json` | +| `threshold_share_decryption_default.json` | +| `threshold_user_data_encryption_ct0_default.json` | +| `threshold_user_data_encryption_ct1_default.json` | + +## Notes + +- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is + effectively 0. diff --git a/circuits/benchmarks/results_secure_no_agg/benchmark_run_meta.json b/circuits/benchmarks/results_secure_no_agg/benchmark_run_meta.json new file mode 100644 index 0000000000..651b5d7722 --- /dev/null +++ b/circuits/benchmarks/results_secure_no_agg/benchmark_run_meta.json @@ -0,0 +1,11 @@ +{ + "benchmark_mode": "secure", + "bfv_preset_subdir": "secure-8192", + "proof_aggregation": false, + "multithread_jobs": 13, + "verbose": true, + "nodes_spawned": 20, + "committee_size_n": 3, + "network_model": "in_process_bus", + "testmode_harness": true +} diff --git a/circuits/benchmarks/results_secure_no_agg/crisp_verify_gas.json b/circuits/benchmarks/results_secure_no_agg/crisp_verify_gas.json new file mode 100644 index 0000000000..5014f0c4f9 --- /dev/null +++ b/circuits/benchmarks/results_secure_no_agg/crisp_verify_gas.json @@ -0,0 +1,88 @@ +{ + "verify_gas": { + "dkg": null, + "user": 2972977, + "dec": null + }, + "source": "folded_proof_export_plus_crisp_verify_test", + "artifact_sizes_bytes": { + "dkg": { + "proof": 0, + "public_inputs": 0 + }, + "dec": { + "proof": 0, + "public_inputs": 0 + } + }, + "calldata_gas": { + "dkg": { + "proof": 0, + "public_inputs": 0, + "total": 0 + }, + "dec": { + "proof": 0, + "public_inputs": 0, + "total": 0 + } + }, + "integration_summary": { + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "secure", + "bfv_preset_subdir": "secure-8192", + "bfv_preset": "SecureThreshold8192", + "lambda": 60, + "proof_aggregation_enabled": false, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": false, + "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, + "operation_timings": [ + { "name": "CalculateDecryptionKey", "avg_seconds": 0.036104014, "runs": 3, "total_seconds": 0.108312043 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.155666888, "runs": 3, "total_seconds": 0.467000666 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.234273375, "runs": 1, "total_seconds": 0.234273375 }, + { "name": "GenEsiSss", "avg_seconds": 0.088555027, "runs": 3, "total_seconds": 0.265665083 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.120120347, "runs": 3, "total_seconds": 0.360361042 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 2.90879575, "runs": 1, "total_seconds": 2.90879575 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 20.727147222, "runs": 6, "total_seconds": 124.362883334 }, + { "name": "ZkPkAggregation", "avg_seconds": 11.371170708, "runs": 1, "total_seconds": 11.371170708 }, + { "name": "ZkPkBfv", "avg_seconds": 3.53991425, "runs": 3, "total_seconds": 10.61974275 }, + { "name": "ZkPkGeneration", "avg_seconds": 96.487730153, "runs": 3, "total_seconds": 289.463190459 }, + { "name": "ZkShareComputation", "avg_seconds": 50.543073166, "runs": 6, "total_seconds": 303.258439 }, + { "name": "ZkShareEncryption", "avg_seconds": 115.255456694, "runs": 36, "total_seconds": 4149.196440999 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 102.677723166, "runs": 3, "total_seconds": 308.0331695 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.103283111, "runs": 3, "total_seconds": 0.309849333 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.313393508, "runs": 5, "total_seconds": 1.566967541 } + ], + "operation_timings_total_seconds": 5202.526261583, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, + { "label": "Setup completed", "seconds": 2.6577995, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 20.181663416, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.001112875, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 11.43493, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 461.185495792, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 461.7008915, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 0.357493625, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.000820875, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 3.068546, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 144.435938625, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 629.335317291, "metric": "wall_clock" } + ], + "folded_artifacts": null + }, + "test_exit_code": { + "crisp": 0, + "folded_export": 0, + "enclave_contracts": 0 + } +} diff --git a/circuits/benchmarks/results_secure_no_agg/integration_summary.json b/circuits/benchmarks/results_secure_no_agg/integration_summary.json new file mode 100644 index 0000000000..57b3f5eb94 --- /dev/null +++ b/circuits/benchmarks/results_secure_no_agg/integration_summary.json @@ -0,0 +1,180 @@ +{ + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "secure", + "bfv_preset_subdir": "secure-8192", + "bfv_preset": "SecureThreshold8192", + "lambda": 60, + "proof_aggregation_enabled": false, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": false, + "multithread": { + "rayon_threads": 13, + "max_simultaneous_rayon_tasks": 13, + "cores_available": 14 + }, + "operation_timings": [ + { + "name": "CalculateDecryptionKey", + "avg_seconds": 0.036104014, + "runs": 3, + "total_seconds": 0.108312043 + }, + { + "name": "CalculateDecryptionShare", + "avg_seconds": 0.155666888, + "runs": 3, + "total_seconds": 0.467000666 + }, + { + "name": "CalculateThresholdDecryption", + "avg_seconds": 0.234273375, + "runs": 1, + "total_seconds": 0.234273375 + }, + { + "name": "GenEsiSss", + "avg_seconds": 0.088555027, + "runs": 3, + "total_seconds": 0.265665083 + }, + { + "name": "GenPkShareAndSkSss", + "avg_seconds": 0.120120347, + "runs": 3, + "total_seconds": 0.360361042 + }, + { + "name": "ZkDecryptedSharesAggregation", + "avg_seconds": 2.90879575, + "runs": 1, + "total_seconds": 2.90879575 + }, + { + "name": "ZkDkgShareDecryption", + "avg_seconds": 20.727147222, + "runs": 6, + "total_seconds": 124.362883334 + }, + { + "name": "ZkPkAggregation", + "avg_seconds": 11.371170708, + "runs": 1, + "total_seconds": 11.371170708 + }, + { + "name": "ZkPkBfv", + "avg_seconds": 3.53991425, + "runs": 3, + "total_seconds": 10.61974275 + }, + { + "name": "ZkPkGeneration", + "avg_seconds": 96.487730153, + "runs": 3, + "total_seconds": 289.463190459 + }, + { + "name": "ZkShareComputation", + "avg_seconds": 50.543073166, + "runs": 6, + "total_seconds": 303.258439 + }, + { + "name": "ZkShareEncryption", + "avg_seconds": 115.255456694, + "runs": 36, + "total_seconds": 4149.196440999 + }, + { + "name": "ZkThresholdShareDecryption", + "avg_seconds": 102.677723166, + "runs": 3, + "total_seconds": 308.0331695 + }, + { + "name": "ZkVerifyShareDecryptionProofs", + "avg_seconds": 0.103283111, + "runs": 3, + "total_seconds": 0.309849333 + }, + { + "name": "ZkVerifyShareProofs", + "avg_seconds": 0.313393508, + "runs": 5, + "total_seconds": 1.566967541 + } + ], + "operation_timings_total_seconds": 5202.526261583, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { + "label": "Starting trbfv actor test", + "seconds": 0e-9, + "metric": "wall_clock" + }, + { + "label": "Setup completed", + "seconds": 2.6577995, + "metric": "wall_clock" + }, + { + "label": "Committee Setup Completed", + "seconds": 20.181663416, + "metric": "wall_clock" + }, + { + "label": "Committee Finalization Complete", + "seconds": 0.001112875, + "metric": "wall_clock" + }, + { + "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", + "seconds": 11.43493, + "metric": "wall_clock" + }, + { + "label": "ThresholdShares -> PublicKeyAggregated", + "seconds": 461.185495792, + "metric": "wall_clock" + }, + { + "label": "E3Request -> PublicKeyAggregated", + "seconds": 461.7008915, + "metric": "wall_clock" + }, + { + "label": "Application CT Gen", + "seconds": 0.357493625, + "metric": "wall_clock" + }, + { + "label": "Running FHE Application", + "seconds": 0.000820875, + "metric": "wall_clock" + }, + { + "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", + "seconds": 3.068546, + "metric": "wall_clock" + }, + { + "label": "Ciphertext published -> PlaintextAggregated", + "seconds": 144.435938625, + "metric": "wall_clock" + }, + { + "label": "Entire Test", + "seconds": 629.335317291, + "metric": "wall_clock" + } + ], + "folded_artifacts": null +} diff --git a/circuits/benchmarks/results_secure_no_agg/report.md b/circuits/benchmarks/results_secure_no_agg/report.md new file mode 100644 index 0000000000..b3d22c5270 --- /dev/null +++ b/circuits/benchmarks/results_secure_no_agg/report.md @@ -0,0 +1,190 @@ +# Enclave ZK Circuit Benchmarks + +**Generated:** 2026-05-23 15:56:04 UTC + +**Git Branch:** `feat/1549` +**Git Commit:** `604ef9af71651ffae19546146b78a7940744741f` + +**Committee Size:** `H=3`, `N=3`, `T=1` + +## Run configuration + +Settings for this benchmark run (integration test + Nargo circuit benches on the same host). + +### Integration test (`test_trbfv_actor`) + +| Setting | Value | +| ----------------------------------------------------- | ------------------------------------ | +| Benchmark mode | `secure` | +| BFV preset (artifacts) | `secure-8192` | +| BFV preset (enum) | `SecureThreshold8192` | +| λ (smudging / error) | 60 | +| Nodes spawned (builder) | 20 | +| Network model | `in_process_bus` | +| Testmode harness | true | +| `proof_aggregation_enabled` | false | +| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | +| Rayon worker threads | 13 | +| CPU cores (host) | 14 | +| `dkg_fold_attestation_verifier` | _(disabled — proof aggregation off)_ | +| Verbose logging (`run_benchmarks.sh --verbose`) | true | + +### Hardware & software (Nargo / Barretenberg host) + +| | | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **CPU** | Apple M4 Pro | +| **CPU cores** | 14 | +| **RAM** | 48.00 GB | +| **OS** | Darwin | +| **Architecture** | arm64 | +| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | +| **Barretenberg** | 3.0.0-nightly.20260102 | + +--- + +## Audit status + +> **Incomplete on-chain verify gas:** 2 of 3 artifact verify-gas values are **N/A**. Re-run +> `./run_benchmarks.sh` and ensure `extract_crisp_verify_gas.sh` completes (CRISP test + +> `test_trbfv_actor` + EVM replay). Calldata gas alone is not sufficient for audit sign-off. + +--- + +## Measurement methodology + +| Metric kind | Source | Meaning | Do **not** use for | +| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | +| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | +| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | +| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | + +**Harness limits (integration):** all ciphernodes share one process and bus +(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare +runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, +commit, and hardware. + +--- + +## Protocol Summary + +### Circuit Benchmarks (isolated Nargo + Barretenberg) + +Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). + +| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | +| -------------------- | ----------- | --------- | ----------- | ---------- | +| C0 | 287764 | 1.43 | 25.44 | 15.88 | +| C1 | 2432074 | 9.38 | 26.85 | 15.88 | +| C2a | 1446348 | 5.27 | 25.40 | 15.88 | +| C2b | 2889001 | 9.76 | 26.04 | 15.88 | +| C3a | 3563512 | 11.04 | 26.42 | 15.88 | +| C3b | 3563512 | 11.04 | 26.42 | 15.88 | +| C4a | 1961956 | 5.97 | 26.68 | 15.88 | +| C4b | 1961956 | 5.97 | 26.68 | 15.88 | +| C5 | 3719555 | 11.05 | 26.65 | 15.88 | +| user_data_encryption | 1678200 | 5.79 | 26.44 | 15.88 | +| C6 | 3001847 | 10.72 | 26.87 | 15.88 | +| C7 | 109424 | 0.52 | 26.65 | 15.88 | + +### Artifacts + +| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | +| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | +| Π_DKG | 15.88 KB | 0.12 KB | N/A | 197956 | N/A | +| Π_user | 15.88 KB | 0.12 KB | 2972977 | 193300 | 3166277 | +| Π_dec | 15.88 KB | 3.25 KB | N/A | 188136 | N/A | + +### Role / Phase / Activity + +| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | +| --------------- | ----- | ----------------------------------------- | -------------- | --------- | ---------- | --------- | +| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 1226.64 s | 127.00 KB | 128.56 KB | +| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 49.16 s | 15.88 KB | 16.00 KB | +| User | P3 | per user input | isolated_nargo | 11.17 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 10.72 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 331.86 s | 15.88 KB | 19.12 KB | +| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 19.35 s | 15.88 KB | 19.12 KB | + +_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **49.00 s** — not +comparable to P2 wall_clock row above._ + +## Integration test (`test_trbfv_actor`) + +### End-to-end phase timings (integration test) + +| Phase | Metric | Duration (s) | +| ------------------------------------------------------------------ | ------------ | ------------ | +| Starting trbfv actor test | `wall_clock` | 0.00 | +| Setup completed | `wall_clock` | 3.29 | +| Committee Setup Completed | `wall_clock` | 20.25 | +| Committee Finalization Complete | `wall_clock` | 0.00 | +| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 49.16 | +| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 1226.64 | +| E3Request -> PublicKeyAggregated | `wall_clock` | 1233.99 | +| Application CT Gen | `wall_clock` | 7.69 | +| Running FHE Application | `wall_clock` | 0.08 | +| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 19.35 | +| Ciphertext published -> PlaintextAggregated | `wall_clock` | 331.86 | +| Entire Test | `wall_clock` | 1597.16 | + +### Multithread job timings (`tracked_job_wall`) + +| Name | Avg (s) | Runs | Total (s) | +| ----------------------------- | ------- | ---- | --------- | +| CalculateDecryptionKey | 0.62 | 3 | 1.87 | +| CalculateDecryptionShare | 2.17 | 3 | 6.52 | +| CalculateThresholdDecryption | 1.95 | 1 | 1.95 | +| GenEsiSss | 0.75 | 3 | 2.25 | +| GenPkShareAndSkSss | 1.55 | 3 | 4.65 | +| ZkDecryptedSharesAggregation | 18.93 | 1 | 18.93 | +| ZkDkgShareDecryption | 55.89 | 6 | 335.31 | +| ZkPkAggregation | 49.00 | 1 | 49.00 | +| ZkPkBfv | 6.07 | 3 | 18.22 | +| ZkPkGeneration | 380.69 | 3 | 1142.08 | +| ZkShareComputation | 118.56 | 6 | 711.37 | +| ZkShareEncryption | 297.63 | 36 | 10714.67 | +| ZkThresholdShareDecryption | 299.47 | 3 | 898.41 | +| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.29 | +| ZkVerifyShareProofs | 0.30 | 5 | 1.48 | + +Sum of tracked job wall time: **13907.01 s** — **not** end-to-end latency (jobs run in parallel up +to `BENCHMARK_MULTITHREAD_JOBS`). + +_Baseline run: node DKG folds and folded Π_DKG / Π_dec export are disabled. Compare with +`BENCHMARK_PROOF_AGGREGATION=true` (default)._ + +### Aggregation jobs (`tracked_job_wall`) + +| Operation | Avg (s) | Runs | Total (s) | +| ---------------------------- | ------- | ---- | --------- | +| ZkDecryptedSharesAggregation | 18.93 | 1 | 18.93 | +| ZkPkAggregation | 49.00 | 1 | 49.00 | + +Sum of aggregation job tracked time: **67.94 s** (parallel CPU work; not P1/P2 wall clock). + +## Raw circuit benchmark JSON (Nargo) + +Source files for the **Circuit Benchmarks** table. Persist this directory with +`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without +re-running the integration test. + +| File | +| ----------------------------------------------------- | +| `config_default.json` | +| `dkg_e_sm_share_computation_default.json` | +| `dkg_pk_default.json` | +| `dkg_share_decryption_default.json` | +| `dkg_share_encryption_default.json` | +| `dkg_sk_share_computation_default.json` | +| `threshold_decrypted_shares_aggregation_default.json` | +| `threshold_pk_aggregation_default.json` | +| `threshold_pk_generation_default.json` | +| `threshold_share_decryption_default.json` | +| `threshold_user_data_encryption_ct0_default.json` | +| `threshold_user_data_encryption_ct1_default.json` | + +## Notes + +- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is + effectively 0. diff --git a/circuits/benchmarks/scripts/benchmark_output_dir.sh b/circuits/benchmarks/scripts/benchmark_output_dir.sh new file mode 100644 index 0000000000..fd94af1b87 --- /dev/null +++ b/circuits/benchmarks/scripts/benchmark_output_dir.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Shared output directory naming for benchmark runs. +# Sourced by run_benchmarks.sh and regenerate_report.sh. +# +# Four default layouts (under circuits/benchmarks/): +# results_insecure_agg | results_insecure_no_agg +# results_secure_agg | results_secure_no_agg + +# Args: +benchmark_results_dir_basename() { + local mode="$1" + local proof_agg="${2:-true}" + local base="${BENCHMARK_OUTPUT_DIR_BASE:-results}" + local suffix="agg" + case "$(echo "$proof_agg" | tr '[:upper:]' '[:lower:]')" in + 0|false|no|off) suffix="no_agg" ;; + esac + echo "${base}_${mode}_${suffix}" +} + +# Full path under BENCHMARKS_DIR (set by caller). +benchmark_results_dir_path() { + local benchmarks_dir="$1" + local mode="$2" + local proof_agg="${3:-true}" + echo "${benchmarks_dir}/$(benchmark_results_dir_basename "$mode" "$proof_agg")" +} + +# Legacy: results_ (no agg suffix). Used as fallback when regenerating old runs. +benchmark_results_dir_legacy_basename() { + local mode="$1" + local base="${BENCHMARK_OUTPUT_DIR_BASE:-results}" + echo "${base}_${mode}" +} diff --git a/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh b/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh index 20ecf8ad72..645c7b5264 100755 --- a/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh +++ b/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh @@ -40,6 +40,8 @@ for path in "${MARKERS[@]}"; do fi done +ACTIVE="${BIN}/.active-preset.json" + if [ ! -f "$STAMP" ]; then missing+=("$STAMP") elif ! jq -e --arg p "$PRESET" '.preset == $p' "$STAMP" >/dev/null 2>&1; then @@ -48,6 +50,15 @@ elif ! jq -e --arg p "$PRESET" '.preset == $p' "$STAMP" >/dev/null 2>&1; then exit 1 fi +if [ ! -f "$ACTIVE" ]; then + missing+=("$ACTIVE") +elif ! jq -e --arg p "$PRESET" '.preset == $p' "$ACTIVE" >/dev/null 2>&1; then + echo "Error: circuits/bin was last built for a different preset (see ${ACTIVE})." >&2 + echo " Fast fix (no full recompile if dist is ready):" >&2 + echo " pnpm build:circuits --preset ${PRESET} --skip-if-built --no-clean --no-clean-targets" >&2 + exit 1 +fi + if [ ${#missing[@]} -gt 0 ]; then echo "Error: circuit artifacts for preset '${PRESET}' are missing or stale." >&2 echo " circuits/bin/target reflects the last preset built; dist/circuits// must exist for this mode." >&2 diff --git a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh index d4a6df7cf4..7cbdd8a9a6 100755 --- a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh +++ b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh @@ -3,6 +3,11 @@ # extract_crisp_verify_gas.sh - Runs CRISP verifier test with gas reporter and emits JSON. # Usage: ./extract_crisp_verify_gas.sh --output [--mode insecure|secure] [--verbose] # [--skip-build] [--force-build] +# +# Integration test env (also set by run_benchmarks.sh): +# BENCHMARK_PROOF_AGGREGATION=true|false — recursive fold + folded Π_DKG/Π_dec (default: true) +# BENCHMARK_MULTITHREAD_JOBS=N — Rayon concurrent ZK jobs (default: 1) +# BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER — EIP-712 verifying contract for fold attestations set -e @@ -113,20 +118,23 @@ else "${SCRIPT_DIR}/ensure_circuit_preset_built.sh" "${ENSURE_ARGS[@]}" echo " [gas] Build artifacts ready." - echo " [gas] Regenerating Honk Solidity verifiers (dkg_aggregator, decryption_aggregator)..." + # Align circuits/bin with PRESET_NAME, then verify preset artifacts. + # insecure: also diff committed Honk .sol (pinned to insecure-512). + # secure: committed .sol stay insecure-only; gas replay deploys fresh verifiers from bin. + echo " [gas] Verifying circuit preset '${PRESET_NAME}' (dist stamp + circuits/bin)..." if [ "$VERBOSE" = true ]; then - echo " [gas] [verbose] Running: pnpm generate:verifiers --no-compile" + echo " [gas] [verbose] Running: pnpm generate:verifiers --check --no-compile --preset ${PRESET_NAME}" ( cd "$REPO_ROOT" && \ - pnpm generate:verifiers --no-compile + pnpm generate:verifiers --check --no-compile --preset "$PRESET_NAME" ) else ( cd "$REPO_ROOT" && \ - pnpm generate:verifiers --no-compile >/dev/null + pnpm generate:verifiers --check --no-compile --preset "$PRESET_NAME" ) fi - echo " [gas] Honk verifiers ready." + echo " [gas] Preset '${PRESET_NAME}' artifacts ready for integration + gas replay." require_preset_artifacts fi @@ -139,21 +147,36 @@ echo " [gas] Running CRISP verifier test for Pi_user gas..." CRISP_TEST_EXIT_CODE=${PIPESTATUS[0]} echo " [gas] CRISP test completed (exit=${CRISP_TEST_EXIT_CODE})." require_preset_artifacts -echo " [gas] Running integration test (test_trbfv_actor) for folded proofs + timings..." +BENCHMARK_PROOF_AGGREGATION="${BENCHMARK_PROOF_AGGREGATION:-true}" +echo " [gas] Running integration test (test_trbfv_actor); proof_aggregation=${BENCHMARK_PROOF_AGGREGATION}, multithread_jobs=${BENCHMARK_MULTITHREAD_JOBS:-1}, profile=release..." ( cd "$REPO_ROOT" && \ - BENCHMARK_MODE="$MODE" BENCHMARK_FOLDED_OUTPUT="$TMP_JSON_FOLDED" BENCHMARK_SUMMARY_OUTPUT="$TMP_JSON_SUMMARY" cargo test -p e3-tests test_trbfv_actor -- --nocapture + BENCHMARK_MODE="$MODE" \ + BENCHMARK_PROOF_AGGREGATION="$BENCHMARK_PROOF_AGGREGATION" \ + BENCHMARK_FOLDED_OUTPUT="$TMP_JSON_FOLDED" \ + BENCHMARK_SUMMARY_OUTPUT="$TMP_JSON_SUMMARY" \ + cargo test --release -p e3-tests test_trbfv_actor -- --nocapture ) 2>&1 | tee "$TMP_LOG_FOLDED" FOLDED_TEST_EXIT_CODE=${PIPESTATUS[0]} echo " [gas] Integration export completed (exit=${FOLDED_TEST_EXIT_CODE})." -echo " [gas] Replaying folded artifacts on EVM verifiers for Pi_DKG/Pi_dec gas..." -( - cd "$ENCLAVE_CONTRACTS_DIR" && \ - BENCHMARK_RAW_DIR="$RAW_DIR" BENCHMARK_GAS_OUTPUT="$TMP_JSON_ENCLAVE" BENCHMARK_FOLDED_JSON="$TMP_JSON_FOLDED" \ - pnpm hardhat run scripts/benchmarkGasFromRaw.ts --network hardhat -) 2>&1 | tee "$TMP_LOG_ENCLAVE" -ENCLAVE_TEST_EXIT_CODE=${PIPESTATUS[0]} -echo " [gas] EVM replay completed (exit=${ENCLAVE_TEST_EXIT_CODE})." +ENCLAVE_TEST_EXIT_CODE=0 +if [ "$FOLDED_TEST_EXIT_CODE" -ne 0 ]; then + echo " [gas] Skipping EVM replay: test_trbfv_actor failed (exit=${FOLDED_TEST_EXIT_CODE})." + echo '{}' >"$TMP_JSON_ENCLAVE" +elif [ ! -s "$TMP_JSON_FOLDED" ] || ! jq -e '(.dkg_aggregator.proof_hex != "") and (.decryption_aggregator.proof_hex != "")' "$TMP_JSON_FOLDED" >/dev/null 2>&1; then + echo " [gas] Skipping EVM replay: folded proof export missing or empty." + echo '{}' >"$TMP_JSON_ENCLAVE" +else + echo " [gas] Replaying folded artifacts on EVM verifiers for Pi_DKG/Pi_dec gas..." + ( + cd "$ENCLAVE_CONTRACTS_DIR" && \ + BENCHMARK_RAW_DIR="$RAW_DIR" BENCHMARK_GAS_OUTPUT="$TMP_JSON_ENCLAVE" BENCHMARK_FOLDED_JSON="$TMP_JSON_FOLDED" \ + BENCHMARK_PRESET="$PRESET_NAME" \ + pnpm hardhat run scripts/benchmarkGasFromRaw.ts --network hardhat + ) 2>&1 | tee "$TMP_LOG_ENCLAVE" + ENCLAVE_TEST_EXIT_CODE=${PIPESTATUS[0]} + echo " [gas] EVM replay completed (exit=${ENCLAVE_TEST_EXIT_CODE})." +fi set -e parse_marker() { @@ -292,3 +315,10 @@ cat > "$OUTPUT_JSON" </dev/null | head -1 +} + integration_timing_seconds() { local label="$1" local val="" - local f + local f blob + if [ -n "${2:-}" ]; then + val=$(integration_phase_seconds "$label" "$2") + [ -n "$val" ] && [ "$val" != "null" ] && echo "$val" && return + fi for f in "$INTEGRATION_SUMMARY_FILE" "$GAS_JSON"; do [ -n "$f" ] && [ -f "$f" ] || continue - val=$(jq -r --arg label "$label" '.integration_summary.timings_seconds[]? | select(.label == $label) | .seconds' "$f" 2>/dev/null | head -1) - if [ -n "$val" ] && [ "$val" != "null" ]; then - echo "$val" - return - fi - val=$(jq -r --arg label "$label" '.timings_seconds[]? | select(.label == $label) | .seconds' "$f" 2>/dev/null | head -1) + blob=$(jq -c 'if (.integration_summary != null) then .integration_summary elif has("integration_test") then . else empty end' "$f" 2>/dev/null || true) + [ -z "$blob" ] || [ "$blob" = "null" ] && continue + val=$(integration_phase_seconds "$label" "$blob") if [ -n "$val" ] && [ "$val" != "null" ]; then echo "$val" return @@ -278,10 +294,64 @@ integration_timing_seconds() { echo "" } +emit_audit_warnings() { + local missing=0 + local v + { + echo "## Audit status" + echo "" + } >> "$OUTPUT_FILE" + for art in "Π_DKG" "Π_user" "Π_dec"; do + v=$(verify_gas_for_artifact "$art") + if [ "$v" = "N/A" ]; then + missing=$((missing + 1)) + fi + done + if [ "$missing" -gt 0 ]; then + cat >> "$OUTPUT_FILE" < **Incomplete on-chain verify gas:** $missing of 3 artifact verify-gas values are **N/A**. Re-run \`./run_benchmarks.sh\` and ensure \`extract_crisp_verify_gas.sh\` completes (CRISP test + \`test_trbfv_actor\` + EVM replay). Calldata gas alone is not sufficient for audit sign-off. + +EOF + else + echo "On-chain verify gas: **complete** (CRISP Π_user + Enclave Π_DKG / Π_dec replay)." >> "$OUTPUT_FILE" + echo "" >> "$OUTPUT_FILE" + fi + if [ -z "$INTEGRATION_BLOB" ]; then + cat >> "$OUTPUT_FILE" < **No integration summary:** Role/phase **wall-clock** rows and multithread job tables require \`integration_summary.json\` or embedded \`integration_summary\` in \`crisp_verify_gas.json\`. + +EOF + fi + echo "---" >> "$OUTPUT_FILE" + echo "" >> "$OUTPUT_FILE" +} + +emit_measurement_methodology() { + cat >> "$OUTPUT_FILE" <<'EOF' +## Measurement methodology + +| Metric kind | Source | Meaning | Do **not** use for | +|-------------|--------|---------|-------------------| +| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | +| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | +| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | + +**Harness limits (integration):** all ciphernodes share one process and bus (`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, commit, and hardware. + +--- +EOF +} + # Normalized integration summary object: either `results_*/integration_summary.json` or # `crisp_verify_gas.json` → `.integration_summary` (see `BENCHMARK_SUMMARY_OUTPUT` in e3-tests). integration_blob_from_inputs() { - local f blob + local f blob sibling + if [ -z "$INTEGRATION_SUMMARY_FILE" ] && [ -n "$GAS_JSON" ] && [ -f "$GAS_JSON" ]; then + sibling="$(dirname "$GAS_JSON")/integration_summary.json" + if [ -f "$sibling" ]; then + INTEGRATION_SUMMARY_FILE="$sibling" + fi + fi for f in "$INTEGRATION_SUMMARY_FILE" "$GAS_JSON"; do [ -n "$f" ] && [ -f "$f" ] || continue blob=$(jq -c 'if (.integration_summary != null) and (.integration_summary | type == "object") then .integration_summary elif has("integration_test") then . else empty end' "$f" 2>/dev/null || true) @@ -307,15 +377,17 @@ artifact_size_pair_from_gas() { fi fi # Fallback: folded hex from integration summary export (test `BENCHMARK_SUMMARY_OUTPUT`). - if [ -n "$INTEGRATION_SUMMARY_FILE" ] && [ -f "$INTEGRATION_SUMMARY_FILE" ]; then + local blob + blob="$(integration_blob_from_inputs 2>/dev/null || true)" + if [ -n "$blob" ]; then local pfx ph pubh case "$key" in dkg) pfx=".folded_artifacts.dkg_aggregator" ;; dec) pfx=".folded_artifacts.decryption_aggregator" ;; *) echo ""; return ;; esac - ph=$(jq -r "${pfx}.proof_hex // empty" "$INTEGRATION_SUMMARY_FILE" 2>/dev/null || true) - pubh=$(jq -r "${pfx}.public_inputs_hex // empty" "$INTEGRATION_SUMMARY_FILE" 2>/dev/null || true) + ph=$(jq -r "${pfx}.proof_hex // empty" <<<"$blob" 2>/dev/null || true) + pubh=$(jq -r "${pfx}.public_inputs_hex // empty" <<<"$blob" 2>/dev/null || true) if [ -n "$ph" ] && [ "$ph" != "null" ] && [ -n "$pubh" ] && [ "$pubh" != "null" ]; then proof=$(hex_len_bytes "$ph") public=$(hex_len_bytes "$pubh") @@ -356,8 +428,184 @@ PY echo "$h|$n|$t" } +load_system_info_from_raw() { + local first_json + first_json=$(ls "$INPUT_DIR"/*.json 2>/dev/null | head -1) + [ -n "$first_json" ] || return 1 + CPU_MODEL=$(jq -r '.system_info.cpu_model // "unknown"' "$first_json") + CPU_CORES=$(jq -r '.system_info.cpu_cores // "unknown"' "$first_json") + RAM_GB=$(jq -r '.system_info.ram_gb // "unknown"' "$first_json") + SYS_OS=$(jq -r '.system_info.os // "unknown"' "$first_json") + SYS_ARCH=$(jq -r '.system_info.arch // "unknown"' "$first_json") + NARGO_VER=$(jq -r '.system_info.nargo_version // "unknown"' "$first_json") + BB_VER=$(jq -r '.system_info.bb_version // "unknown"' "$first_json") +} + +resolve_run_meta_file() { + if [ -n "$RUN_META_FILE" ] && [ -f "$RUN_META_FILE" ]; then + echo "$RUN_META_FILE" + return 0 + fi + local candidate + for candidate in \ + "$(dirname "$INPUT_DIR")/benchmark_run_meta.json" \ + "$(dirname "$GAS_JSON")/benchmark_run_meta.json"; do + if [ -n "$candidate" ] && [ -f "$candidate" ]; then + echo "$candidate" + return 0 + fi + done + return 1 +} + +emit_run_configuration_section() { + local blob="${1:-}" + local meta_file + meta_file="$(resolve_run_meta_file 2>/dev/null || true)" + + local bench_mode bench_preset bench_preset_name bench_lambda + local proof_agg multithread_jobs rayon_threads cores_avail fold_verifier + local entire_test_sec verbose_flag integration_test_name + + bench_mode="$BENCHMARK_MODE_OVERRIDE" + bench_preset="" + bench_preset_name="" + bench_lambda="" + proof_agg="" + multithread_jobs="" + rayon_threads="" + cores_avail="" + fold_verifier="" + entire_test_sec="" + integration_test_name="test_trbfv_actor" + verbose_flag="" + + if [ -n "$blob" ]; then + bench_mode=$(jq -r '.benchmark_config.mode // empty' <<<"$blob") + bench_preset=$(jq -r '.benchmark_config.bfv_preset_subdir // empty' <<<"$blob") + bench_preset_name=$(jq -r '.benchmark_config.bfv_preset // empty' <<<"$blob") + bench_lambda=$(jq -r '.benchmark_config.lambda // empty' <<<"$blob") + # NOTE: cannot use `//` here — jq's alt-operator treats `false` as null + # and would fall through, dropping the legitimate `false` value. + proof_agg=$(jq -r ' + if .benchmark_config.proof_aggregation_enabled != null then + .benchmark_config.proof_aggregation_enabled + elif .proof_aggregation_enabled != null then + .proof_aggregation_enabled + else + empty + end + ' <<<"$blob") + multithread_jobs=$(jq -r '.benchmark_config.multithread_concurrent_jobs // .multithread.max_simultaneous_rayon_tasks // empty' <<<"$blob") + rayon_threads=$(jq -r '.multithread.rayon_threads // empty' <<<"$blob") + cores_avail=$(jq -r '.multithread.cores_available // empty' <<<"$blob") + fold_verifier=$(jq -r '.dkg_fold_attestation_verifier // empty' <<<"$blob") + entire_test_sec=$(jq -r '.timings_seconds[]? | select(.label == "Entire Test") | .seconds' <<<"$blob" | head -1) + integration_test_name=$(jq -r '.integration_test // "test_trbfv_actor"' <<<"$blob") + fi + + if [ -n "$meta_file" ]; then + [ -z "$bench_mode" ] || [ "$bench_mode" = "null" ] && bench_mode=$(jq -r '.benchmark_mode // empty' "$meta_file") + [ -z "$bench_preset" ] || [ "$bench_preset" = "null" ] && bench_preset=$(jq -r '.bfv_preset_subdir // empty' "$meta_file") + [ -z "$proof_agg" ] || [ "$proof_agg" = "null" ] && proof_agg=$(jq -r 'if .proof_aggregation != null then .proof_aggregation else empty end' "$meta_file") + [ -z "$multithread_jobs" ] || [ "$multithread_jobs" = "null" ] && multithread_jobs=$(jq -r '.multithread_jobs // empty' "$meta_file") + verbose_flag=$(jq -r 'if .verbose != null then .verbose else empty end' "$meta_file") + fi + + [ -z "$bench_mode" ] || [ "$bench_mode" = "null" ] && bench_mode="unknown" + [ -z "$bench_preset" ] || [ "$bench_preset" = "null" ] && bench_preset="(see circuit artifacts)" + [ -z "$proof_agg" ] || [ "$proof_agg" = "null" ] && proof_agg="unknown" + [ -z "$multithread_jobs" ] || [ "$multithread_jobs" = "null" ] && multithread_jobs="1 (default)" + [ -z "$rayon_threads" ] || [ "$rayon_threads" = "null" ] && rayon_threads="N/A" + [ -z "$cores_avail" ] || [ "$cores_avail" = "null" ] && cores_avail="N/A" + + load_system_info_from_raw 2>/dev/null || true + CPU_MODEL="${CPU_MODEL:-unknown}" + CPU_CORES="${CPU_CORES:-unknown}" + RAM_GB="${RAM_GB:-unknown}" + SYS_OS="${SYS_OS:-unknown}" + SYS_ARCH="${SYS_ARCH:-unknown}" + NARGO_VER="${NARGO_VER:-unknown}" + BB_VER="${BB_VER:-unknown}" + + { + echo "## Run configuration" + echo "" + echo "Settings for this benchmark run (integration test + Nargo circuit benches on the same host)." + echo "" + echo "### Integration test (\`$integration_test_name\`)" + echo "" + echo "| Setting | Value |" + echo "|---------|-------|" + echo "| Benchmark mode | \`$bench_mode\` |" + echo "| BFV preset (artifacts) | \`$bench_preset\` |" + if [ -n "$bench_preset_name" ] && [ "$bench_preset_name" != "null" ]; then + echo "| BFV preset (enum) | \`$bench_preset_name\` |" + fi + if [ -n "$bench_lambda" ] && [ "$bench_lambda" != "null" ]; then + echo "| λ (smudging / error) | $bench_lambda |" + fi + local nodes_spawned network_model testmode_harness + nodes_spawned=$(jq -r '.benchmark_config.nodes_spawned // empty' <<<"$blob") + network_model=$(jq -r '.benchmark_config.network_model // empty' <<<"$blob") + testmode_harness=$(jq -r 'if .benchmark_config.testmode_harness != null then .benchmark_config.testmode_harness else empty end' <<<"$blob") + if [ -n "$nodes_spawned" ] && [ "$nodes_spawned" != "null" ]; then + echo "| Nodes spawned (builder) | $nodes_spawned |" + fi + if [ -n "$network_model" ] && [ "$network_model" != "null" ]; then + echo "| Network model | \`$network_model\` |" + fi + if [ -n "$testmode_harness" ] && [ "$testmode_harness" != "null" ]; then + echo "| Testmode harness | $testmode_harness |" + fi + echo "| \`proof_aggregation_enabled\` | $proof_agg |" + echo "| \`BENCHMARK_MULTITHREAD_JOBS\` (max concurrent ZK jobs) | $multithread_jobs |" + echo "| Rayon worker threads | $rayon_threads |" + echo "| CPU cores (host) | $cores_avail |" + if [ -n "$fold_verifier" ] && [ "$fold_verifier" != "null" ]; then + echo "| \`dkg_fold_attestation_verifier\` (EIP-712) | \`$fold_verifier\` |" + elif [ "$proof_agg" = "false" ]; then + echo "| \`dkg_fold_attestation_verifier\` | _(disabled — proof aggregation off)_ |" + fi + if [ -n "$entire_test_sec" ] && [ "$entire_test_sec" != "null" ]; then + echo "| Integration wall clock (\`Entire Test\`) | $(format_s "$entire_test_sec") s |" + fi + if [ -n "$verbose_flag" ] && [ "$verbose_flag" != "null" ]; then + echo "| Verbose logging (\`run_benchmarks.sh --verbose\`) | $verbose_flag |" + fi + echo "" + echo "### Hardware & software (Nargo / Barretenberg host)" + echo "" + echo "| | |" + echo "|--|--|" + echo "| **CPU** | $CPU_MODEL |" + echo "| **CPU cores** | $CPU_CORES |" + echo "| **RAM** | ${RAM_GB} GB |" + echo "| **OS** | $SYS_OS |" + echo "| **Architecture** | $SYS_ARCH |" + echo "| **Nargo** | $NARGO_VER |" + echo "| **Barretenberg** | $BB_VER |" + echo "" + echo "---" + echo "" + } >> "$OUTPUT_FILE" +} + +integration_op_total_seconds() { + local pattern="$1" + local blob="${2:-}" + [ -z "$blob" ] && return 1 + jq -r --arg p "$pattern" ' + [.operation_timings[]? + | select(.name | test($p)) + | .total_seconds] + | add // empty + ' <<<"$blob" 2>/dev/null || true +} + TIMESTAMP=$(date -u "+%Y-%m-%d %H:%M:%S UTC") IFS='|' read -r PROTOCOL_H PROTOCOL_N PROTOCOL_T <<< "$(load_protocol_params)" +INTEGRATION_BLOB="$(integration_blob_from_inputs || true)" cat > "$OUTPUT_FILE" < "$OUTPUT_FILE" </dev/null || true) + if [ "$fold_exit" != "" ] && [ "$fold_exit" != "null" ] && [ "$fold_exit" != "0" ]; then + cat >> "$OUTPUT_FILE" < **Warning:** \`test_trbfv_actor\` failed during gas extraction (exit ${fold_exit}). Π_DKG / Π_dec verify gas and phase rows below may reflect **Nargo-only** estimates or stale data. Re-run \`./run_benchmarks.sh\` after a successful integration export. +EOF + fi +fi + +cat >> "$OUTPUT_FILE" < PublicKeyAggregated") +p1_metric="wall_clock" +p2_metric="wall_clock" +p3_metric="isolated_nargo" +p4n_metric="isolated_nargo" +p4a_metric="wall_clock" + +p1_integration=$(integration_timing_seconds "ThresholdShares -> PublicKeyAggregated" "$INTEGRATION_BLOB") if [ -n "$p1_integration" ] && [ "$p1_integration" != "null" ]; then p1t="$(format_s "$p1_integration") s" +else + p1t="N/A" + p1_metric="—" fi -p4a_integration=$(integration_timing_seconds "Ciphertext published -> PlaintextAggregated") + +p2_integration=$(integration_timing_seconds "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)" "$INTEGRATION_BLOB") +if [ -n "$p2_integration" ] && [ "$p2_integration" != "null" ]; then + p2t="$(format_s "$p2_integration") s" +else + p2t="N/A" + p2_metric="—" +fi + +p4a_integration=$(integration_timing_seconds "Ciphertext published -> PlaintextAggregated" "$INTEGRATION_BLOB") if [ -n "$p4a_integration" ] && [ "$p4a_integration" != "null" ]; then p4at="$(format_s "$p4a_integration") s" +else + p4at="N/A" + p4a_metric="—" +fi + +p4_sub_integration=$(integration_timing_seconds "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)" "$INTEGRATION_BLOB") +p4_sub_metric="" +p4_sub_t="" +if [ -n "$p4_sub_integration" ] && [ "$p4_sub_integration" != "null" ]; then + p4_sub_t="$(format_s "$p4_sub_integration") s" + p4_sub_metric="wall_clock" fi # Keep role-phase rows aligned with artifact outputs when folded artifact sizes are available. @@ -447,52 +740,51 @@ cat >> "$OUTPUT_FILE" <> "$OUTPUT_FILE" +fi +if [ -n "$INTEGRATION_BLOB" ]; then + p2_tracked=$(integration_op_total_seconds "^(ZkDkgAggregation|ZkPkAggregation)" "$INTEGRATION_BLOB") + if [ -n "$p2_tracked" ] && [ "$p2_tracked" != "null" ]; then + echo "" >> "$OUTPUT_FILE" + echo "_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **$(format_s "$p2_tracked") s** — not comparable to P2 wall_clock row above._" >> "$OUTPUT_FILE" + fi +fi -INTEGRATION_BLOB="$(integration_blob_from_inputs || true)" if [ -n "$INTEGRATION_BLOB" ]; then it_name=$(jq -r '.integration_test // "test_trbfv_actor"' <<<"$INTEGRATION_BLOB") { echo "" echo "## Integration test (\`$it_name\`)" echo "" - echo "### End-to-end phase timings (wall clock)" + echo "### End-to-end phase timings (integration test)" echo "" - echo "| Phase | Duration (s) |" - echo "|-------|---------------|" + echo "| Phase | Metric | Duration (s) |" + echo "|-------|--------|---------------|" } >> "$OUTPUT_FILE" - while IFS=$'\t' read -r label sec; do + while IFS=$'\t' read -r label metric sec; do [ -z "$label" ] && continue - echo "| $label | $(format_s "$sec") |" >> "$OUTPUT_FILE" - done < <(jq -r '.timings_seconds[]? | [.label, .seconds] | @tsv' <<<"$INTEGRATION_BLOB") - - if jq -e '.multithread != null' <<<"$INTEGRATION_BLOB" >/dev/null 2>&1; then - rt=$(jq -r '.multithread.rayon_threads' <<<"$INTEGRATION_BLOB") - mx=$(jq -r '.multithread.max_simultaneous_rayon_tasks' <<<"$INTEGRATION_BLOB") - cr=$(jq -r '.multithread.cores_available' <<<"$INTEGRATION_BLOB") - { - echo "" - echo "### Thread pool (same process as integration test)" - echo "" - echo "| Setting | Value |" - echo "|---------|-------|" - echo "| Rayon threads | $rt |" - echo "| Max simultaneous Rayon tasks | $mx |" - echo "| Cores available | $cr |" - } >> "$OUTPUT_FILE" - fi + [ -z "$metric" ] || [ "$metric" = "null" ] && metric="wall_clock" + echo "| $label | \`$metric\` | $(format_s "$sec") |" >> "$OUTPUT_FILE" + done < <(jq -r ' + (.phase_timings // .timings_seconds // [])[] + | if type == "object" then [.label, (.metric // "wall_clock"), .seconds] + else [.label, "wall_clock", .] end + | @tsv + ' <<<"$INTEGRATION_BLOB") if jq -e '(.operation_timings | type == "array") and (.operation_timings | length > 0)' <<<"$INTEGRATION_BLOB" >/dev/null 2>&1; then { echo "" - echo "### CPU-bound operation timings (tracked in-process)" + echo "### Multithread job timings (\`tracked_job_wall\`)" echo "" echo "| Name | Avg (s) | Runs | Total (s) |" echo "|------|---------|------|-----------|" @@ -504,9 +796,103 @@ if [ -n "$INTEGRATION_BLOB" ]; then ott=$(jq -r '.operation_timings_total_seconds // empty' <<<"$INTEGRATION_BLOB") if [ -n "$ott" ] && [ "$ott" != "null" ]; then echo "" >> "$OUTPUT_FILE" - echo "Sum of tracked operation wall time: **$(format_s "$ott") s** (often much larger than end-to-end wall clock because work runs in parallel)." >> "$OUTPUT_FILE" + echo "Sum of tracked job wall time: **$(format_s "$ott") s** — **not** end-to-end latency (jobs run in parallel up to \`BENCHMARK_MULTITHREAD_JOBS\`)." >> "$OUTPUT_FILE" + fi + fold_rows=$( + jq -r ' + .operation_timings[]? + | select(.name | startswith("NodeDkgFold/")) + | [.name, .avg_seconds, .runs, .total_seconds] + | @tsv + ' <<<"$INTEGRATION_BLOB" + ) + if [ -n "$fold_rows" ]; then + { + echo "" + echo "### NodeDkgFold sub-steps (\`tracked_job_wall\`, per fold prove)" + echo "" + echo "| Step | Avg (s) | Runs | Total (s) |" + echo "|------|---------|------|-----------|" + } >> "$OUTPUT_FILE" + while IFS=$'\t' read -r name avgr runs tot; do + [ -z "$name" ] && continue + step="${name#NodeDkgFold/}" + echo "| $step | $(format_s "$avgr") | $runs | $(format_s "$tot") |" >> "$OUTPUT_FILE" + done <<<"$fold_rows" fi fi + + # NOTE: cannot use `// true` here — jq's `//` treats `false` as null and + # would incorrectly return `true` when aggregation is explicitly disabled. + agg_enabled=$(jq -r 'if .proof_aggregation_enabled == false then "false" else "true" end' <<<"$INTEGRATION_BLOB") + if [ "$agg_enabled" = "false" ]; then + echo "" >> "$OUTPUT_FILE" + echo "_Baseline run: node DKG folds and folded Π_DKG / Π_dec export are disabled. Compare with \`BENCHMARK_PROOF_AGGREGATION=true\` (default)._" >> "$OUTPUT_FILE" + fi + + if jq -e '(.operation_timings | type == "array") and (.operation_timings | length > 0)' <<<"$INTEGRATION_BLOB" >/dev/null 2>&1; then + agg_rows=$( + jq -r ' + .operation_timings[]? + | select( + .name + | test("^(ZkNodeDkgFold|ZkDkgAggregation|ZkDecryptionAggregation|ZkPkAggregation|ZkDecryptedSharesAggregation|ZkNodesFold)$") + ) + | [.name, .avg_seconds, .runs, .total_seconds] + | @tsv + ' <<<"$INTEGRATION_BLOB" + ) + if [ -n "$agg_rows" ]; then + { + echo "" + echo "### Aggregation jobs (\`tracked_job_wall\`)" + echo "" + echo "| Operation | Avg (s) | Runs | Total (s) |" + echo "|-----------|---------|------|-----------|" + } >> "$OUTPUT_FILE" + while IFS=$'\t' read -r name avgr runs tot; do + [ -z "$name" ] && continue + echo "| $name | $(format_s "$avgr") | $runs | $(format_s "$tot") |" >> "$OUTPUT_FILE" + done <<<"$agg_rows" + agg_total=$( + jq -r ' + [.operation_timings[]? + | select( + .name + | test("^(ZkNodeDkgFold|ZkDkgAggregation|ZkDecryptionAggregation|ZkPkAggregation|ZkDecryptedSharesAggregation|ZkNodesFold)$") + ) + | .total_seconds] + | add // 0 + ' <<<"$INTEGRATION_BLOB" + ) + if [ -n "$agg_total" ] && [ "$agg_total" != "null" ]; then + echo "" >> "$OUTPUT_FILE" + echo "Sum of aggregation job tracked time: **$(format_s "$agg_total") s** (parallel CPU work; not P1/P2 wall clock)." >> "$OUTPUT_FILE" + fi + fi + fi + + if jq -e '.folded_artifacts != null' <<<"$INTEGRATION_BLOB" >/dev/null 2>&1; then + { + echo "" + echo "### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas)" + echo "" + echo "| Artifact | Proof (bytes) | Public inputs (bytes) |" + echo "|----------|---------------|------------------------|" + } >> "$OUTPUT_FILE" + for key in dkg_aggregator decryption_aggregator; do + pb=$(jq -r ".folded_artifacts.${key}.proof_hex // empty" <<<"$INTEGRATION_BLOB") + pubb=$(jq -r ".folded_artifacts.${key}.public_inputs_hex // empty" <<<"$INTEGRATION_BLOB") + if [ -n "$pb" ] && [ ${#pb} -gt 2 ]; then + proof_bytes=$(( (${#pb} - 2) / 2 )) + pub_bytes=$(( (${#pubb} - 2) / 2 )) + echo "| $key | $proof_bytes | $pub_bytes |" >> "$OUTPUT_FILE" + fi + done + elif [ "$agg_enabled" = "true" ]; then + echo "" >> "$OUTPUT_FILE" + echo "_No \`folded_artifacts\` in integration summary (export failed or test exited early)._" >> "$OUTPUT_FILE" + fi fi { @@ -529,32 +915,6 @@ else fi shopt -u nullglob -first_json=$(ls "$INPUT_DIR"/*.json 2>/dev/null | head -1) -if [ -n "$first_json" ]; then - cpu_model=$(jq -r '.system_info.cpu_model // "unknown"' "$first_json") - cpu_cores=$(jq -r '.system_info.cpu_cores // "unknown"' "$first_json") - ram_gb=$(jq -r '.system_info.ram_gb // "unknown"' "$first_json") - os=$(jq -r '.system_info.os // "unknown"' "$first_json") - arch=$(jq -r '.system_info.arch // "unknown"' "$first_json") - nargo=$(jq -r '.system_info.nargo_version // "unknown"' "$first_json") - bb=$(jq -r '.system_info.bb_version // "unknown"' "$first_json") - cat >> "$OUTPUT_FILE" <> "$OUTPUT_FILE" </ +if [ ! -d "${OUTPUT_DIR}/raw" ] && [ ! -f "${OUTPUT_DIR}/crisp_verify_gas.json" ]; then + LEGACY="${BENCHMARKS_DIR}/$(benchmark_results_dir_legacy_basename "$MODE")" + if [ -d "${LEGACY}/raw" ] || [ -f "${LEGACY}/crisp_verify_gas.json" ]; then + echo "Note: using legacy output dir ${LEGACY} (rename to $(basename "$OUTPUT_DIR") to match new layout)" + OUTPUT_DIR="$LEGACY" + fi +fi GIT_COMMIT=$(git -C "$REPO_ROOT" rev-parse HEAD 2>/dev/null || echo "unknown") GIT_BRANCH=$(git -C "$REPO_ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") GAS_JSON="${OUTPUT_DIR}/crisp_verify_gas.json" INTEGRATION_JSON="${OUTPUT_DIR}/integration_summary.json" +RUN_META="${OUTPUT_DIR}/benchmark_run_meta.json" GR=( "${SCRIPT_DIR}/generate_report.sh" --input-dir "${OUTPUT_DIR}/raw" --output "${OUTPUT_DIR}/report.md" --git-commit "$GIT_COMMIT" --git-branch "$GIT_BRANCH" --gas-json "$GAS_JSON" + --benchmark-mode "$MODE" ) +if [ -f "$RUN_META" ]; then + GR+=(--run-meta "$RUN_META") +fi if [ -f "$INTEGRATION_JSON" ]; then GR+=(--integration-summary "$INTEGRATION_JSON") fi diff --git a/circuits/benchmarks/scripts/replay_folded_verify_gas.sh b/circuits/benchmarks/scripts/replay_folded_verify_gas.sh index 76e0b95940..68cff81f0d 100755 --- a/circuits/benchmarks/scripts/replay_folded_verify_gas.sh +++ b/circuits/benchmarks/scripts/replay_folded_verify_gas.sh @@ -6,7 +6,7 @@ # Usage (from repo root): # ./circuits/benchmarks/scripts/replay_folded_verify_gas.sh \ # --summary /tmp/summary_secure.json \ -# --gas-json ./circuits/benchmarks/results_secure/crisp_verify_gas.json \ +# --gas-json ./circuits/benchmarks/results_secure_agg/crisp_verify_gas.json \ # --build secure-8192 # # Use --build when Hardhat reverts with SumcheckFailed (verifier VKs must match the @@ -87,8 +87,8 @@ if [ -n "$BUILD_PRESET" ]; then ENSURE_ARGS+=(--force-build) fi "${SCRIPT_DIR}/ensure_circuit_preset_built.sh" "${ENSURE_ARGS[@]}" - echo " [replay-gas] Regenerating Honk Solidity verifiers (pnpm generate:verifiers --no-compile)..." - (cd "$REPO_ROOT" && pnpm generate:verifiers --no-compile) + echo " [replay-gas] Verifying preset '${BUILD_PRESET}' (dist stamp + circuits/bin)..." + (cd "$REPO_ROOT" && pnpm generate:verifiers --check --no-compile --preset "$BUILD_PRESET") fi fi @@ -98,6 +98,7 @@ echo " [replay-gas] Running Hardhat benchmarkGasFromRaw.ts (folded proofs)..." BENCHMARK_RAW_DIR="$RAW_DIR" \ BENCHMARK_GAS_OUTPUT="$TMP_GAS_PARTIAL" \ BENCHMARK_FOLDED_JSON="$TMP_FOLDED" \ + BENCHMARK_PRESET="${BUILD_PRESET:-insecure-512}" \ pnpm hardhat run scripts/benchmarkGasFromRaw.ts --network hardhat ) diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index c5a9a9f5d8..a99939275f 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -1,12 +1,16 @@ #!/bin/bash # run_benchmarks.sh - Main orchestration script for benchmarking circuits -# Usage: ./run_benchmarks.sh [--config ] [--mode insecure|secure] [--circuit ] [--skip-compile] [--bench-compile] [--clean] [--verbose] +# Usage: ./run_benchmarks.sh [--config ] [--mode insecure|secure] [--circuit ] +# [--skip-compile] [--bench-compile] [--clean] [--verbose] +# [--proof-aggregation on|off] [--multithread-jobs N] set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BENCHMARKS_DIR="$(dirname "$SCRIPT_DIR")" +# shellcheck source=benchmark_output_dir.sh +source "${SCRIPT_DIR}/benchmark_output_dir.sh" CONFIG_FILE="${BENCHMARKS_DIR}/config.json" CLEAN_ARTIFACTS=false MODE_OVERRIDE="" @@ -15,6 +19,8 @@ BENCH_COMPILE=false CIRCUIT_FILTER="" VERBOSE=false PRESET_ARTIFACTS_READY=false +PROOF_AGGREGATION="${BENCHMARK_PROOF_AGGREGATION:-true}" +MULTITHREAD_JOBS="${BENCHMARK_MULTITHREAD_JOBS:-}" # Parse arguments while [[ $# -gt 0 ]]; do @@ -51,14 +57,44 @@ while [[ $# -gt 0 ]]; do VERBOSE=true shift ;; + --proof-aggregation) + PROOF_AGGREGATION="$2" + shift 2 + ;; + --no-proof-aggregation) + PROOF_AGGREGATION=false + shift + ;; + --multithread-jobs) + MULTITHREAD_JOBS="$2" + shift 2 + ;; *) echo "Unknown option: $1" - echo "Usage: $0 [--config ] [--mode insecure|secure] [--circuit ] [--skip-compile] [--bench-compile] [--clean] [--verbose]" + echo "Usage: $0 [--config ] [--mode insecure|secure] [--circuit ] [--skip-compile] [--bench-compile] [--clean] [--verbose] [--proof-aggregation on|off] [--no-proof-aggregation] [--multithread-jobs N]" exit 1 ;; esac done +case "$(echo "$PROOF_AGGREGATION" | tr '[:upper:]' '[:lower:]')" in + 0|false|no|off) export BENCHMARK_PROOF_AGGREGATION=false ;; + 1|true|yes|on|"") export BENCHMARK_PROOF_AGGREGATION=true ;; + *) + echo "Error: --proof-aggregation must be on or off (got: $PROOF_AGGREGATION)" + exit 1 + ;; +esac +if [ -n "$MULTITHREAD_JOBS" ]; then + if ! [[ "$MULTITHREAD_JOBS" =~ ^[1-9][0-9]*$ ]]; then + echo "Error: --multithread-jobs must be a positive integer (got: $MULTITHREAD_JOBS)" + exit 1 + fi + export BENCHMARK_MULTITHREAD_JOBS="$MULTITHREAD_JOBS" +elif [ -n "${BENCHMARK_MULTITHREAD_JOBS:-}" ]; then + echo " Using BENCHMARK_MULTITHREAD_JOBS from environment: $BENCHMARK_MULTITHREAD_JOBS" +fi + if [ ! -f "$CONFIG_FILE" ]; then echo "Error: Config file not found: $CONFIG_FILE" exit 1 @@ -102,8 +138,9 @@ REPO_ROOT="$(cd "${BENCHMARKS_DIR}/../.." && pwd)" # Circuits live under circuits/bin (bin_dir is relative to benchmarks dir, e.g. ../bin) CIRCUITS_BASE_DIR="$(cd "${BENCHMARKS_DIR}/${BIN_DIR}" && pwd)" -# Create mode-specific output directory -OUTPUT_DIR="${OUTPUT_DIR_BASE}_${MODE}" +# results__agg | results__no_agg (see benchmark_output_dir.sh) +BENCHMARK_OUTPUT_DIR_BASE="$OUTPUT_DIR_BASE" +OUTPUT_DIR="$(benchmark_results_dir_basename "$MODE" "$BENCHMARK_PROOF_AGGREGATION")" mkdir -p "${BENCHMARKS_DIR}/${OUTPUT_DIR}/raw" # For secure mode, patch lib to use secure configs (restored at end) @@ -139,6 +176,10 @@ fi if [ "$VERBOSE" = true ]; then echo " Verbose Logging: Yes" fi +echo " Proof aggregation (integration): $BENCHMARK_PROOF_AGGREGATION" +if [ -n "$BENCHMARK_MULTITHREAD_JOBS" ]; then + echo " Multithread concurrent jobs: $BENCHMARK_MULTITHREAD_JOBS" +fi echo " Git Branch: $GIT_BRANCH" echo " Git Commit: $GIT_COMMIT" echo " Circuits: $(echo $CIRCUITS | wc -w | tr -d ' ')" @@ -268,15 +309,44 @@ else echo "⚠️ Could not extract CRISP verify gas; report will show N/A for verify gas" fi +# Persist CLI flags for report regeneration (see generate_report.sh → Run configuration). +RUN_META_FILE="${BENCHMARKS_DIR}/${OUTPUT_DIR}/benchmark_run_meta.json" +MT_JOBS_JSON="${BENCHMARK_MULTITHREAD_JOBS:-1}" +jq -n \ + --arg mode "$MODE" \ + --arg preset "$([ "$MODE" = "secure" ] && echo "secure-8192" || echo "insecure-512")" \ + --argjson proof_agg "$( [ "$BENCHMARK_PROOF_AGGREGATION" = "false" ] && echo false || echo true )" \ + --argjson multithread_jobs "$MT_JOBS_JSON" \ + --argjson verbose "$([ "$VERBOSE" = true ] && echo true || echo false)" \ + '{ + benchmark_mode: $mode, + bfv_preset_subdir: $preset, + proof_aggregation: $proof_agg, + multithread_jobs: $multithread_jobs, + verbose: $verbose, + nodes_spawned: 20, + committee_size_n: 3, + network_model: "in_process_bus", + testmode_harness: true + }' > "${RUN_META_FILE}" + # Generate markdown report echo "Stage 2/3: Rendering markdown report from benchmarks + gas summary..." REPORT_FILE="${BENCHMARKS_DIR}/${OUTPUT_DIR}/report.md" -"${SCRIPT_DIR}/generate_report.sh" \ - --input-dir "${BENCHMARKS_DIR}/${OUTPUT_DIR}/raw" \ - --output "${REPORT_FILE}" \ - --git-commit "$GIT_COMMIT" \ - --git-branch "$GIT_BRANCH" \ +INTEGRATION_SNAPSHOT="${BENCHMARKS_DIR}/${OUTPUT_DIR}/integration_summary.json" +REPORT_ARGS=( + --input-dir "${BENCHMARKS_DIR}/${OUTPUT_DIR}/raw" + --output "${REPORT_FILE}" + --git-commit "$GIT_COMMIT" + --git-branch "$GIT_BRANCH" --gas-json "${GAS_JSON_FILE}" + --benchmark-mode "$MODE" + --run-meta "${RUN_META_FILE}" +) +if [ -f "${INTEGRATION_SNAPSHOT}" ]; then + REPORT_ARGS+=(--integration-summary "${INTEGRATION_SNAPSHOT}") +fi +"${SCRIPT_DIR}/generate_report.sh" "${REPORT_ARGS[@]}" INTEGRATION_SNAPSHOT="${BENCHMARKS_DIR}/${OUTPUT_DIR}/integration_summary.json" if [ -f "${GAS_JSON_FILE}" ] && jq -e '.integration_summary != null' "${GAS_JSON_FILE}" >/dev/null 2>&1; then @@ -284,7 +354,7 @@ if [ -f "${GAS_JSON_FILE}" ] && jq -e '.integration_summary != null' "${GAS_JSON echo "✓ Wrote integration summary snapshot: ${INTEGRATION_SNAPSHOT}" fi -if [ "${OUTPUT_DIR}" = "results_insecure" ] && [ -f "${INTEGRATION_SNAPSHOT}" ]; then +if [ "${OUTPUT_DIR}" = "results_insecure_agg" ] && [ -f "${INTEGRATION_SNAPSHOT}" ]; then "${SCRIPT_DIR}/sync_bfv_vk_binding_fixture.sh" "${INTEGRATION_SNAPSHOT}" fi diff --git a/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh b/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh index 353a68b3e8..f21bfff8f1 100755 --- a/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh +++ b/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash # Sync packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json -# from circuits/benchmarks/results_insecure/integration_summary.json (.folded_artifacts). +# from circuits/benchmarks/results_insecure_agg/integration_summary.json (.folded_artifacts). set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" -INTEGRATION_JSON="${1:-${REPO_ROOT}/circuits/benchmarks/results_insecure/integration_summary.json}" +INTEGRATION_JSON="${1:-${REPO_ROOT}/circuits/benchmarks/results_insecure_agg/integration_summary.json}" FIXTURE="${REPO_ROOT}/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json" if [ ! -f "${INTEGRATION_JSON}" ]; then diff --git a/crates/Dockerfile b/crates/Dockerfile index b651fcf9ce..2724d7cd9c 100644 --- a/crates/Dockerfile +++ b/crates/Dockerfile @@ -6,6 +6,7 @@ COPY packages/enclave-react ./packages/enclave-react COPY packages/enclave-sdk ./packages/enclave-sdk COPY packages/enclave-config ./packages/enclave-config COPY packages/enclave-contracts ./packages/enclave-contracts +COPY package.json . COPY pnpm-workspace.yaml . COPY pnpm-lock.yaml . diff --git a/crates/aggregator/Cargo.toml b/crates/aggregator/Cargo.toml index 078dc82ccb..4204aee146 100644 --- a/crates/aggregator/Cargo.toml +++ b/crates/aggregator/Cargo.toml @@ -28,6 +28,7 @@ e3-request = { workspace = true } e3-sortition = { workspace = true } e3-zk-helpers = { workspace = true } e3-utils = { workspace = true } +e3-zk-prover = { workspace = true } futures = { workspace = true } serde = { workspace = true } tracing = { workspace = true } diff --git a/crates/aggregator/src/committee.rs b/crates/aggregator/src/committee.rs index f77289debd..4f92e0f8b3 100644 --- a/crates/aggregator/src/committee.rs +++ b/crates/aggregator/src/committee.rs @@ -7,9 +7,12 @@ use alloy::primitives::Address; use anyhow::{anyhow, Result}; use e3_events::OrderedSet; +use std::collections::HashMap; use std::str::FromStr; -/// Parse ordered committee node strings (`topNodes` / `PublicKeyAggregated.nodes`) once at ingress. +/// Parse committee node strings from an [`OrderedSet`] (address-sorted iteration). +/// +/// Prefer [`committee_addresses_in_party_order`] for ZK / on-chain committee-hash binding. pub fn committee_addresses_from_nodes(nodes: &OrderedSet) -> Result> { nodes .iter() @@ -18,3 +21,66 @@ pub fn committee_addresses_from_nodes(nodes: &OrderedSet) -> Result, +) -> Result> { + party_ids + .iter() + .map(|party_id| { + let node = party_nodes + .get(party_id) + .ok_or_else(|| anyhow!("missing committee node for party_id {party_id}"))?; + Address::from_str(node) + .map_err(|e| anyhow!("invalid committee node address {node}: {e}")) + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy::primitives::address; + use e3_utils::committee_hash::hash_committee_addresses; + + #[test] + fn party_order_differs_from_address_sorted_set() { + let party_nodes = HashMap::from([ + ( + 0u64, + "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc".to_string(), + ), + ( + 1u64, + "0x90F79bf6EB2c4f870365E785982E1f101E93b906".to_string(), + ), + ( + 2u64, + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65".to_string(), + ), + ]); + let party_ids = vec![0, 1, 2]; + + let party_order = committee_addresses_in_party_order(&party_ids, &party_nodes).unwrap(); + let mut nodes = OrderedSet::new(); + for node in party_nodes.values() { + nodes.insert(node.clone()); + } + let address_order = committee_addresses_from_nodes(&nodes).unwrap(); + + assert_ne!(party_order, address_order); + assert_eq!( + hash_committee_addresses(&party_order), + hash_committee_addresses(&[ + address!("0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc"), + address!("0x90F79bf6EB2c4f870365E785982E1f101E93b906"), + address!("0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65"), + ]) + ); + } +} diff --git a/crates/aggregator/src/ext.rs b/crates/aggregator/src/ext.rs index c2fdc5e1f2..b1886f12d8 100644 --- a/crates/aggregator/src/ext.rs +++ b/crates/aggregator/src/ext.rs @@ -186,6 +186,9 @@ fn load_committee_addresses(ctx: &E3Context, e3_id: &E3id) -> Result Result { let _ = ctx.set_dependency(COMMITTEE_ADDRESSES_KEY, addrs); } diff --git a/crates/aggregator/src/publickey_aggregator.rs b/crates/aggregator/src/publickey_aggregator.rs index 4f9324572f..5bd76cb39c 100644 --- a/crates/aggregator/src/publickey_aggregator.rs +++ b/crates/aggregator/src/publickey_aggregator.rs @@ -4,9 +4,10 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::committee::committee_addresses_from_nodes; +use crate::committee::committee_addresses_in_party_order; use actix::prelude::*; -use anyhow::Result; +use alloy::primitives::Address; +use anyhow::{anyhow, bail, ensure, Context as _, Result}; use e3_data::Persistable; use e3_events::{ prelude::*, BusHandle, CircuitName, ComputeRequest, ComputeRequestError, ComputeResponse, @@ -14,14 +15,16 @@ use e3_events::{ DkgAggregationRequest, E3Failed, E3Stage, E3id, EnclaveEvent, EnclaveEventData, EventContext, FailureReason, KeyshareCreated, OrderedSet, PartyProofsToVerify, PkAggregationProofPending, PkAggregationProofRequest, PkAggregationProofSigned, Proof, ProofType, PublicKeyAggregated, - Seed, Sequenced, ShareVerificationComplete, ShareVerificationDispatched, SignedProofFailed, - SignedProofPayload, TypedEvent, VerificationKind, ZkRequest, ZkResponse, + Seed, Sequenced, ShareVerificationComplete, ShareVerificationDispatched, + SignedDkgFoldAttestation, SignedProofFailed, SignedProofPayload, TypedEvent, VerificationKind, + ZkRequest, ZkResponse, }; use e3_events::{trap, EType}; use e3_fhe::{Fhe, GetAggregatePublicKey}; use e3_fhe_params::BfvPreset; use e3_utils::NotifySync; use e3_utils::{ArcBytes, MAILBOX_LIMIT}; +use e3_zk_prover::extract_node_fold_agg_commits; use std::collections::{BTreeSet, HashMap}; use std::sync::Arc; use tracing::{error, info, warn}; @@ -30,6 +33,42 @@ use tracing::{error, info, warn}; /// Must stay in lock-step with the Noir circuit's output ABI declaration. const C5_PK_COMMITMENT_FIELD: &str = "commitment"; +fn verify_dkg_fold_attestation( + e3_id: &E3id, + party_id: u64, + proof: &Proof, + attestation: &e3_events::SignedDkgFoldAttestation, + expected_node: &str, + committee_n: usize, + committee_h: usize, + n_moduli: usize, +) -> Result<()> { + ensure!( + attestation.payload.e3_id == *e3_id, + "attestation e3_id mismatch" + ); + ensure!( + attestation.payload.party_id == party_id, + "attestation party_id mismatch" + ); + let expected: Address = expected_node + .parse() + .with_context(|| format!("invalid committee node address {expected_node}"))?; + ensure!( + attestation.verify_signer(&expected)?, + "fold attestation signer does not match committee node for party {party_id}" + ); + let (extracted_party, commits) = + extract_node_fold_agg_commits(proof, committee_n, committee_h, n_moduli) + .map_err(|e| anyhow!("{e}"))?; + ensure!(extracted_party == party_id, "NodeFold party_id mismatch"); + ensure!( + commits == attestation.payload.agg_commits, + "NodeFold commits do not match signed attestation" + ); + Ok(()) +} + /// Extract the hash-based aggregated PK commitment from the signed C5 proof. /// This is the last public signal of `CircuitName::PkAggregation`. fn extract_pk_commitment(c5_proof: &Proof) -> Result<[u8; 32]> { @@ -78,8 +117,13 @@ pub enum PublicKeyAggregatorState { public_key: ArcBytes, keyshare_bytes: Vec, nodes: OrderedSet, + /// Registered node address per sortition `party_id` (for fold attestation checks). + party_nodes: HashMap, /// DKG recursive proofs per party (restart-critical). dkg_node_proofs: HashMap>, + /// Per-party fold attestations collected with honest DKG folds. + #[serde(default)] + dkg_fold_attestations: HashMap, honest_party_ids: BTreeSet, dishonest_parties: BTreeSet, /// In-flight [`ZkRequest::DkgAggregation`], if any. @@ -93,6 +137,9 @@ pub enum PublicKeyAggregatorState { public_key: ArcBytes, keyshares: OrderedSet, nodes: OrderedSet, + /// Ascending `party_id` order (matches on-chain `topNodes` after finalize sort). + #[serde(default)] + committee_addresses: Vec
, }, } @@ -107,6 +154,16 @@ impl PublicKeyAggregatorState { } } + pub fn committee_addresses(&self) -> Option<&[Address]> { + match self { + PublicKeyAggregatorState::Complete { + committee_addresses, + .. + } if !committee_addresses.is_empty() => Some(committee_addresses.as_slice()), + _ => None, + } + } + pub fn init(threshold_n: usize, threshold_m: usize, seed: Seed) -> Self { PublicKeyAggregatorState::Collecting { threshold_n, @@ -305,6 +362,7 @@ impl PublicKeyAggregator { }; let mut dishonest_parties = msg.dishonest_parties.clone(); + let committee_n = submission_order.len(); // Filter out parties that failed C1 ZK verification. Keyed by the real // sortition party_id carried in `submission_order`, not arrival index. @@ -428,6 +486,24 @@ impl PublicKeyAggregator { return Ok(()); } + // C5 (PkAggregation) is compiled for a fixed committee size H; partial committees fail witness encoding. + if honest_keyshares.len() != committee_n { + error!( + "C5 requires all {committee_n} committee parties with valid C1 proofs; only {} honest after verification (dishonest: {:?})", + honest_keyshares.len(), + dishonest_parties + ); + self.bus.publish( + E3Failed { + e3_id: self.e3_id.clone(), + failed_at_stage: E3Stage::CommitteeFinalized, + reason: FailureReason::DKGInvalidShares, + }, + ec, + )?; + return Ok(()); + } + // Synchronous aggregation info!( "Aggregating public key from {} honest shares...", @@ -466,12 +542,19 @@ impl PublicKeyAggregator { ec.clone(), )?; + let party_nodes: HashMap = honest_entries + .iter() + .map(|(pid, node, _, _)| (*pid, node.clone())) + .collect(); + self.state.try_mutate(&ec, |_| { Ok(PublicKeyAggregatorState::GeneratingC5Proof { public_key: pubkey.clone(), keyshare_bytes, nodes: honest_nodes_set, + party_nodes, dkg_node_proofs: HashMap::new(), + dkg_fold_attestations: HashMap::new(), honest_party_ids: honest_party_ids.clone(), dishonest_parties: dishonest_parties.clone(), dkg_aggregation_correlation: None, @@ -519,7 +602,9 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation, @@ -533,7 +618,9 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation, @@ -559,7 +646,10 @@ impl PublicKeyAggregator { let state = self.state.get(); let Some(PublicKeyAggregatorState::GeneratingC5Proof { - dkg_node_proofs, .. + party_nodes, + dkg_node_proofs, + honest_party_ids, + .. }) = state.as_ref() else { info!( @@ -577,6 +667,62 @@ impl PublicKeyAggregator { return Ok(()); } + if honest_party_ids.contains(&msg.party_id) { + let Some(expected_node) = party_nodes.get(&msg.party_id) else { + warn!( + party_id = msg.party_id, + "DKG fold from party without registered node address — rejecting" + ); + return Ok(()); + }; + // Proof aggregation OFF: nodes emit `DKGRecursiveAggregationComplete` + // with `proof=None` and `attestation=None`. Accept it so + // `try_publish_complete` can detect `all_proofs_are_none` and publish. + // Proof aggregation ON: both must be present and verified together. + match (&msg.aggregated_proof, &msg.fold_attestation) { + (None, None) => { + // no-aggregation mode — skip attestation verification + } + (Some(proof), Some(attestation)) => { + let meta = self.params_preset.metadata(); + let committee_n = party_nodes.len(); + let committee_h = committee_n; + let n_moduli = meta.num_moduli as usize; + if let Err(e) = verify_dkg_fold_attestation( + &self.e3_id, + msg.party_id, + proof, + attestation, + expected_node, + committee_n, + committee_h, + n_moduli, + ) { + warn!( + party_id = msg.party_id, + error = %e, + "DKG fold attestation verification failed — rejecting" + ); + return Ok(()); + } + } + (Some(_), None) => { + warn!( + party_id = msg.party_id, + "DKG fold has proof but missing attestation — rejecting (attribution)" + ); + return Ok(()); + } + (None, Some(_)) => { + warn!( + party_id = msg.party_id, + "DKG fold has attestation but missing proof — rejecting" + ); + return Ok(()); + } + } + } + info!( "PublicKeyAggregator: buffered DKG proof from party {} (buffered={})", msg.party_id, @@ -588,7 +734,9 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, mut dkg_node_proofs, + mut dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation, @@ -600,11 +748,16 @@ impl PublicKeyAggregator { return Ok(state); }; dkg_node_proofs.insert(msg.party_id, msg.aggregated_proof); + if let Some(attestation) = msg.fold_attestation.clone() { + dkg_fold_attestations.insert(msg.party_id, attestation); + } Ok(PublicKeyAggregatorState::GeneratingC5Proof { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation, @@ -621,7 +774,7 @@ impl PublicKeyAggregator { fn try_dispatch_dkg_aggregation(&mut self, ec: &EventContext) -> Result<()> { let state = self.state.get(); let Some(PublicKeyAggregatorState::GeneratingC5Proof { - nodes, + party_nodes, dkg_node_proofs, honest_party_ids, c5_proof_pending, @@ -680,7 +833,9 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: _, @@ -696,7 +851,9 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: None, @@ -737,13 +894,12 @@ impl PublicKeyAggregator { return Ok(()); } - let committee_addresses = committee_addresses_from_nodes(nodes)?; + let committee_addresses = committee_addresses_in_party_order(&party_ids, party_nodes)?; #[cfg(debug_assertions)] { - let n_registered = committee_addresses.len(); debug_assert_eq!( party_ids.len(), - n_registered, + committee_addresses.len(), "honest NodeFold count must equal registered committee size until expulsion enables H < N" ); } @@ -769,7 +925,9 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: _, @@ -784,7 +942,9 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: Some(corr), @@ -811,6 +971,9 @@ impl PublicKeyAggregator { let PublicKeyAggregatorState::GeneratingC5Proof { public_key, nodes, + party_nodes, + dkg_fold_attestations, + honest_party_ids, c5_proof_pending, dkg_aggregated_proof, dkg_aggregation_correlation: _, @@ -867,12 +1030,29 @@ impl PublicKeyAggregator { let pk_commitment = extract_pk_commitment(c5_proof)?; + let party_ids: Vec = honest_party_ids.iter().copied().collect(); + let committee_addresses = committee_addresses_in_party_order(&party_ids, &party_nodes)?; + + let dkg_attestation_bundle = match dkg_aggregated_proof.as_ref() { + Some(_) => { + let bundle = e3_evm::encode_dkg_attestation_bundle( + &honest_party_ids, + &party_nodes, + &dkg_fold_attestations, + )?; + Some(ArcBytes::from_bytes(&bundle)) + } + None => None, + }; + let event = PublicKeyAggregated { pubkey: public_key.clone(), e3_id: self.e3_id.clone(), nodes: nodes.clone(), + committee_addresses: committee_addresses.clone(), pk_commitment, dkg_aggregator_proof: dkg_aggregated_proof.clone(), + dkg_attestation_bundle, }; self.bus.publish(event, ec.clone())?; @@ -881,6 +1061,7 @@ impl PublicKeyAggregator { public_key, keyshares: OrderedSet::new(), nodes, + committee_addresses, }) })?; @@ -908,7 +1089,9 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation, @@ -924,7 +1107,9 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation, @@ -937,7 +1122,9 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: None, @@ -989,7 +1176,9 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: _, @@ -1005,7 +1194,9 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: None, @@ -1321,7 +1512,9 @@ mod tests { public_key: ArcBytes::from_bytes(&[1, 2, 3]), keyshare_bytes: Vec::new(), nodes: OrderedSet::new(), + party_nodes: HashMap::new(), dkg_node_proofs: HashMap::new(), + dkg_fold_attestations: HashMap::new(), honest_party_ids: BTreeSet::new(), dishonest_parties: BTreeSet::new(), dkg_aggregation_correlation: Some(correlation_id), @@ -1465,4 +1658,48 @@ mod tests { Ok(()) } + + #[actix::test] + async fn honest_dkg_fold_without_attestation_is_not_buffered() -> Result<()> { + let correlation_id = CorrelationId::new(); + let mut initial_state = generating_c5_state(correlation_id); + let PublicKeyAggregatorState::GeneratingC5Proof { + ref mut party_nodes, + ref mut honest_party_ids, + .. + } = initial_state + else { + unreachable!(); + }; + honest_party_ids.insert(2); + party_nodes.insert(2, "0x70997970C51812dc3A010C7d01b50e0d17dc79C8".to_string()); + + let (mut aggregator, _history, e3_id) = build_public_key_aggregator(initial_state).await?; + let ec = test_ctx(DKGRecursiveAggregationComplete { + e3_id: e3_id.clone(), + party_id: 2, + aggregated_proof: Some(dummy_proof(CircuitName::NodeFold)), + fold_attestation: None, + }); + + aggregator.handle_dkg_recursive_aggregation_complete(TypedEvent::new( + DKGRecursiveAggregationComplete { + e3_id: e3_id.clone(), + party_id: 2, + aggregated_proof: Some(dummy_proof(CircuitName::NodeFold)), + fold_attestation: None, + }, + ec, + ))?; + + let Some(PublicKeyAggregatorState::GeneratingC5Proof { + dkg_node_proofs, .. + }) = aggregator.state.get() + else { + panic!("expected GeneratingC5Proof state"); + }; + assert!(!dkg_node_proofs.contains_key(&2)); + + Ok(()) + } } diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index 8c0e0bbbbf..a3ae8c55d0 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -6,6 +6,7 @@ use crate::{CiphernodeHandle, EventSystem, EvmSystemChainBuilder, ProviderCache, WriteEnabled}; use actix::{Actor, Addr}; +use alloy::primitives::Address; use alloy::signers::local::PrivateKeySigner; use anyhow::Result; use derivative::Derivative; @@ -17,10 +18,10 @@ use e3_data::{InMemStore, RepositoriesFactory}; use e3_events::{ AggregateConfig, AggregateId, BusHandle, EnclaveEvent, EventBus, EventBusConfig, EvmEventConfig, }; -use e3_evm::{BondingRegistrySolReader, CiphernodeRegistrySolReader, EnclaveSolWriter}; use e3_evm::{ - CiphernodeRegistrySol, EnclaveSolReader, ProviderConfig, SlashingManagerSolReader, - SlashingManagerSolWriter, + fetch_accusation_vote_validity, fetch_dkg_fold_attestation_verifier, BondingRegistrySolReader, + CiphernodeRegistrySol, CiphernodeRegistrySolReader, EnclaveSolReader, EnclaveSolWriter, + ProviderConfig, SlashingManagerSolReader, SlashingManagerSolWriter, }; use e3_fhe::ext::FheExtension; use e3_keyshare::ext::ThresholdKeyshareExtension; @@ -43,7 +44,7 @@ use e3_zk_prover::{ use libp2p::PeerId; use std::time::Duration; use std::{collections::HashMap, path::PathBuf, sync::Arc}; -use tracing::{error, info}; +use tracing::{error, info, warn}; #[derive(Clone, Debug)] enum EventSystemType { @@ -81,6 +82,8 @@ pub struct CiphernodeBuilder { testmode_signer: Option, threshold_plaintext_agg: bool, zk_backend: Option, + /// Test/benchmark: EIP-712 verifying contract for accusation votes (no RPC required). + slashing_manager: Option
, net_config: Option, ignore_address_check: bool, global_shared_store: bool, @@ -149,6 +152,7 @@ impl CiphernodeBuilder { threads: None, testmode_signer: None, threshold_plaintext_agg: false, + slashing_manager: None, net_config: None, zk_backend: None, ignore_address_check: false, @@ -213,6 +217,98 @@ impl CiphernodeBuilder { self } + /// Benchmark/test: set slashing manager address (EIP-712 verifyingContract for + /// accusation votes) without configuring EVM chains (no RPC). + pub fn testmode_with_slashing_manager(mut self, slashing_manager: Address) -> Self { + self.slashing_manager = Some(slashing_manager); + self + } + + fn resolve_slashing_manager(&self) -> Result
{ + if let Some(addr) = self.slashing_manager { + return Ok(addr); + } + self.chains + .first() + .and_then(|c| c.contracts.slashing_manager.as_ref()) + .map(|c| c.address()) + .transpose()? + .ok_or_else(|| { + anyhow::anyhow!( + "`slashing_manager` contract address is required in chain config — \ + it is the EIP-712 `verifyingContract` for accusation vote signatures" + ) + }) + } + + /// Fetch `CiphernodeRegistry.dkgFoldAttestationVerifier()` for one chain (EIP-712 verifying contract). + async fn fetch_dkg_fold_attestation_verifier_from_registry( + provider_cache: &mut ProviderCache, + chain: &ChainConfig, + ) -> Result> { + let provider = provider_cache.ensure_read_provider(chain).await?; + let registry = chain.contracts.ciphernode_registry.address()?; + let verifier = fetch_dkg_fold_attestation_verifier(provider.provider(), registry).await?; + if verifier.is_none() { + tracing::warn!( + chain = %chain.name, + registry = %registry, + "CiphernodeRegistry.dkgFoldAttestationVerifier is not set on-chain; \ + nodes will not sign DKG fold attestations when proof aggregation is enabled" + ); + } else if let Some(addr) = verifier { + info!( + chain = %chain.name, + registry = %registry, + verifier = %addr, + "loaded dkgFoldAttestationVerifier from CiphernodeRegistry" + ); + } + Ok(verifier) + } + + /// Fetch `CiphernodeRegistry.accusationVoteValidity()` for one chain (off-chain + /// vote freshness window in seconds). Returns `0` when the registry has + /// disabled slashing (governance emergency stop). The actor will then refuse + /// to stamp votes that would be rejected on chain. + /// + /// The `u256` returned by the registry is clamped to `u64`. The contract + /// has no upper bound but `u64::MAX` seconds is already ~5.8 × 10¹¹ years — + /// any value that doesn't fit in `u64` is treated as "effectively infinite" + /// by saturating at `u64::MAX`, matching the on-chain `block.timestamp` + /// comparison. + async fn fetch_accusation_vote_validity_from_registry( + provider_cache: &mut ProviderCache, + chain: &ChainConfig, + ) -> Result { + let provider = provider_cache.ensure_read_provider(chain).await?; + let registry = chain.contracts.ciphernode_registry.address()?; + let validity = fetch_accusation_vote_validity(provider.provider(), registry).await?; + let secs = match validity { + Some(v) => { + let clamped: u64 = v.try_into().unwrap_or(u64::MAX); + info!( + chain = %chain.name, + registry = %registry, + accusation_vote_validity_secs = clamped, + "loaded accusationVoteValidity from CiphernodeRegistry" + ); + clamped + } + None => { + tracing::warn!( + chain = %chain.name, + registry = %registry, + "CiphernodeRegistry.accusationVoteValidity is 0; the off-chain \ + accusation manager will not produce votes (governance-disabled \ + or pre-initialized registry)" + ); + 0 + } + }; + Ok(secs) + } + /// Log data actor events pub fn with_logging(mut self) -> Self { self.logging = true; @@ -250,6 +346,27 @@ impl CiphernodeBuilder { self } + /// Configure the Rayon compute pool for production workloads. + /// + /// Reserves `reserve_threads` CPUs for Actix / networking, uses the remainder for Rayon, and + /// allows up to `concurrent_jobs` CPU-bound tasks at once (ZK + TrBFV). When `concurrent_jobs` + /// is `None`, uses all available compute threads. + pub fn with_multithread_config( + mut self, + reserve_threads: usize, + concurrent_jobs: Option, + ) -> Self { + let max_threads = Multithread::get_max_threads_minus(reserve_threads); + let jobs = concurrent_jobs.unwrap_or(max_threads).max(1); + let pool_threads = jobs.min(max_threads).max(1); + info!( + "Multithread pool: rayon_threads={pool_threads}, max_concurrent_jobs={jobs}, reserve_threads={reserve_threads}" + ); + self.threads = Some(pool_threads); + self.multithread_concurrent_jobs = Some(jobs); + self + } + /// This will save the given number of threads from being used by the rayon threadpool pub fn with_max_threads_minus(mut self, threads: usize) -> Self { self.threads = Some(Multithread::get_max_threads_minus(threads)); @@ -463,6 +580,55 @@ impl CiphernodeBuilder { ) .await?; + let needs_zk_actors = + self.keyshare.is_some() || (self.pubkey_agg && self.keyshare.is_none()); + let mut dkg_fold_verifier_by_chain: HashMap> = HashMap::new(); + if needs_zk_actors { + if !self.chains.is_empty() { + for chain in self.chains.iter().filter(|c| c.enabled.unwrap_or(true)) { + let provider = provider_cache.ensure_read_provider(chain).await?; + let chain_id = provider.chain_id(); + validate_chain_id(chain, chain_id)?; + let verifier = Self::fetch_dkg_fold_attestation_verifier_from_registry( + &mut provider_cache, + chain, + ) + .await?; + dkg_fold_verifier_by_chain.insert(chain_id, verifier); + } + } else { + // In-process benchmark harness (no EVM chains): optional env override. + if let Some(verifier) = std::env::var("BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER") + .ok() + .and_then(|s| s.parse().ok()) + { + dkg_fold_verifier_by_chain.insert(benchmark_default_chain_id(), Some(verifier)); + } + } + } + + // Off-chain freshness window for accusation votes — fetched per chain so + // AccusationManagerExtension can look up by `e3_id.chain_id()`. Benchmark + // harness falls back to the env var so deterministic builds don't require RPC. + let mut accusation_vote_validity_by_chain: HashMap = HashMap::new(); + if !self.chains.is_empty() { + for chain in self.chains.iter().filter(|c| c.enabled.unwrap_or(true)) { + let provider = provider_cache.ensure_read_provider(chain).await?; + let chain_id = provider.chain_id(); + validate_chain_id(chain, chain_id)?; + let validity = + Self::fetch_accusation_vote_validity_from_registry(&mut provider_cache, chain) + .await?; + accusation_vote_validity_by_chain.insert(chain_id, validity); + } + } else { + let validity = std::env::var("BENCHMARK_ACCUSATION_VOTE_VALIDITY_SECS") + .ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or(0); + accusation_vote_validity_by_chain.insert(benchmark_default_chain_id(), validity); + } + // E3 specific setup let mut e3_builder = E3Router::builder(&bus, store.clone()); @@ -486,7 +652,7 @@ impl CiphernodeBuilder { )); info!("Setting up ZK actors"); - setup_zk_actors(&bus, backend, signer); + setup_zk_actors(&bus, backend, signer, dkg_fold_verifier_by_chain.clone()); } if self.pubkey_agg { @@ -507,7 +673,7 @@ impl CiphernodeBuilder { .ok_or_else(|| anyhow::anyhow!("ZK backend is required for aggregator"))?; let signer = provider_cache.ensure_signer().await?; info!("Setting up ZK actors for aggregator"); - setup_zk_actors(&bus, backend, signer); + setup_zk_actors(&bus, backend, signer, dkg_fold_verifier_by_chain.clone()); } } @@ -519,11 +685,37 @@ impl CiphernodeBuilder { )) } + // Clock-skew allowance for peer accusation deadlines. + let accusation_deadline_skew_secs = match std::env::var("ACCUSATION_DEADLINE_SKEW_SECS") { + Ok(raw) => match raw.parse::() { + Ok(v) => v, + Err(err) => { + warn!( + value = %raw, + error = %err, + "invalid ACCUSATION_DEADLINE_SKEW_SECS; falling back to default" + ); + 30 + } + }, + Err(_) => 30, + }; + // AccusationManager extension — per-E3 fault attribution quorum { let signer = provider_cache.ensure_signer().await?; - info!("Setting up AccusationManagerExtension"); - e3_builder = e3_builder.with(AccusationManagerExtension::create(&bus, signer)); + let slashing_manager_addr = self.resolve_slashing_manager()?; + info!( + chains = accusation_vote_validity_by_chain.len(), + accusation_deadline_skew_secs, "Setting up AccusationManagerExtension" + ); + e3_builder = e3_builder.with(AccusationManagerExtension::create( + &bus, + signer, + slashing_manager_addr, + accusation_vote_validity_by_chain, + accusation_deadline_skew_secs, + )); } // CommitmentConsistencyChecker extension — per-E3 cross-circuit commitment validation @@ -587,10 +779,10 @@ impl CiphernodeBuilder { // Setup threadpool if not set let task_pool = self.task_pool.clone().unwrap_or_else(|| { - Multithread::create_taskpool( - self.threads.unwrap_or(1), - self.multithread_concurrent_jobs.unwrap_or(1), - ) + let pool_threads = self.threads.unwrap_or(1); + let concurrent_jobs = self.multithread_concurrent_jobs.unwrap_or(1); + let pool_threads = concurrent_jobs.min(pool_threads).max(1); + Multithread::create_taskpool(pool_threads, concurrent_jobs) }); // Create it with or without ZK prover @@ -622,6 +814,11 @@ impl CiphernodeBuilder { } } +/// Chain id used by in-process benchmark harnesses when no EVM chains are configured. +fn benchmark_default_chain_id() -> u64 { + 1 +} + /// Validate chain ID matches expected configuration fn validate_chain_id(chain: &ChainConfig, actual_chain_id: u64) -> Result<()> { if let Some(expected_chain_id) = chain.chain_id { diff --git a/crates/config/src/app_config.rs b/crates/config/src/app_config.rs index ef9f061496..b407431b60 100644 --- a/crates/config/src/app_config.rs +++ b/crates/config/src/app_config.rs @@ -53,6 +53,16 @@ pub struct NodeDefinition { pub autowallet: bool, /// Optional dashboard port. When set, serves a monitoring web UI on this port. pub dashboard_port: Option, + /// Logical CPUs reserved for Actix, libp2p, and RPC (not used by the Rayon compute pool). + #[serde(default = "default_multithread_reserve_threads")] + pub multithread_reserve_threads: usize, + /// Max concurrent CPU-bound jobs (ZK proofs + TrBFV). When unset, defaults to all CPUs minus + /// `multithread_reserve_threads`. Override with env `E3_NODE__MULTITHREAD_CONCURRENT_JOBS`. + pub multithread_concurrent_jobs: Option, +} + +fn default_multithread_reserve_threads() -> usize { + 1 } impl Default for NodeDefinition { @@ -71,6 +81,8 @@ impl Default for NodeDefinition { autopassword: false, autowallet: false, dashboard_port: None, + multithread_reserve_threads: default_multithread_reserve_threads(), + multithread_concurrent_jobs: None, } } } @@ -389,6 +401,17 @@ impl AppConfig { pub fn dashboard_port(&self) -> Option { self.node_def().dashboard_port } + + /// CPUs reserved for non-compute work (Actix, networking, RPC). + pub fn multithread_reserve_threads(&self) -> usize { + self.node_def().multithread_reserve_threads + } + + /// Optional cap on concurrent ZK / TrBFV pool jobs. When `None`, the node uses all CPUs minus + /// [`Self::multithread_reserve_threads`]. + pub fn multithread_concurrent_jobs(&self) -> Option { + self.node_def().multithread_concurrent_jobs + } } #[derive(Debug, Deserialize, Serialize)] @@ -781,6 +804,25 @@ chains: }); } + #[test] + fn test_multithread_config() -> Result<()> { + let config_str = r#" +node: + multithread_reserve_threads: 2 + multithread_concurrent_jobs: 4 +"#; + let unscoped: UnscopedAppConfig = serde_yaml::from_str(config_str)?; + let config = unscoped.into_scoped_with_defaults( + "_default", + &PathBuf::from("/default/data"), + &PathBuf::from("/default/config"), + &PathBuf::from("/my/cwd"), + )?; + assert_eq!(config.multithread_reserve_threads(), 2); + assert_eq!(config.multithread_concurrent_jobs(), Some(4)); + Ok(()) + } + #[test] fn test_config_env_vars() { Jail::expect_with(|jail| { diff --git a/crates/entrypoint/src/start/start.rs b/crates/entrypoint/src/start/start.rs index 8c35c90633..cadfbddefb 100644 --- a/crates/entrypoint/src/start/start.rs +++ b/crates/entrypoint/src/start/start.rs @@ -12,7 +12,7 @@ use e3_zk_prover::ZkBackend; use rand::SeedableRng; use rand_chacha::rand_core::OsRng; use std::sync::{Arc, Mutex}; -use tracing::instrument; +use tracing::{info, instrument}; #[instrument(name = "app", skip_all)] pub async fn execute(config: &AppConfig) -> Result { @@ -20,13 +20,22 @@ pub async fn execute(config: &AppConfig) -> Result { let cipher = Arc::new(Cipher::from_file(&config.key_file()).await?); let backend = ZkBackend::new(config.bb_binary(), config.circuits_dir(), config.work_dir()); + let reserve = config.multithread_reserve_threads(); + let concurrent_jobs = config.multithread_concurrent_jobs(); + info!( + "Ciphernode multithread: reserve_threads={reserve}, concurrent_jobs={}", + concurrent_jobs + .map(|n| n.to_string()) + .unwrap_or_else(|| "auto (CPUs - reserve)".to_string()) + ); + let node = CiphernodeBuilder::new(rng.clone(), cipher.clone()) .with_persistence(&config.log_file(), &config.db_file()) .with_sortition_score() .with_chains(&config.chains()) .with_contract_enclave_full() .with_contract_bonding_registry() - .with_max_threads() + .with_multithread_config(reserve, concurrent_jobs) .with_contract_ciphernode_registry() .with_contract_slashing_manager() .with_trbfv() diff --git a/crates/events/src/enclave_event/accusation_quorum_reached.rs b/crates/events/src/enclave_event/accusation_quorum_reached.rs index c4911859ca..bd2e4570f9 100644 --- a/crates/events/src/enclave_event/accusation_quorum_reached.rs +++ b/crates/events/src/enclave_event/accusation_quorum_reached.rs @@ -6,7 +6,7 @@ use crate::{AccusationVote, E3id, ProofType}; use actix::Message; -use alloy::primitives::Address; +use alloy::primitives::{Address, Bytes}; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display}; @@ -15,7 +15,13 @@ use std::fmt::{self, Display}; pub enum AccusationOutcome { /// >= M nodes agree the proof is bad → slash the accused. AccusedFaulted, - /// Only the accuser says bad, same data_hash as others → accuser lied. + /// **Deprecated.** Previously emitted when `votes_against >= M`. The + /// `AccusationVote` gossip wire no longer carries disagreement + /// signatures (a peer who finds the proof passes simply stays silent), + /// so this outcome is no longer produced by the off-chain quorum + /// protocol. Kept in the enum for serialized-event backwards + /// compatibility — downstream consumers should treat any historic + /// `AccuserLied` event the same as `Inconclusive`. AccuserLied, /// data_hashes differ between voters → accused sent different data to different nodes. Equivocation, @@ -49,11 +55,23 @@ pub struct AccusationQuorumReached { /// Which proof type was disputed. pub proof_type: ProofType, /// Votes from nodes that agreed the proof is bad. + /// + /// There is no `votes_against` companion: the gossip protocol no longer + /// carries disagreement signatures, so silence is the only signal of + /// disagreement. See the `AccusationVote` docstring for the rationale. pub votes_for: Vec, - /// Votes from nodes that said the proof is fine. - pub votes_against: Vec, /// The quorum decision. pub outcome: AccusationOutcome, + /// Raw `abi.encode(proof.data, public_signals)` — preimage of every voter's + /// `data_hash`. + /// + /// Consumed by Lane A slash submission: on-chain verifier checks + /// `keccak256(evidence) == sharedDataHash`. + /// Empty when this node didn't have raw bytes locally (e.g. + /// consistency-violation path), in which case submitter should skip + /// submission because on-chain binding cannot be proven. + #[serde(default)] + pub evidence: Bytes, } impl Display for AccusationQuorumReached { diff --git a/crates/events/src/enclave_event/accusation_vote.rs b/crates/events/src/enclave_event/accusation_vote.rs index c07a49731a..510644d1e8 100644 --- a/crates/events/src/enclave_event/accusation_vote.rs +++ b/crates/events/src/enclave_event/accusation_vote.rs @@ -11,10 +11,45 @@ use e3_utils::ArcBytes; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display}; -/// Broadcast via gossip: a committee member's vote on an accusation. +/// EIP-712 domain `name` for accusation vote signatures. /// -/// Each committee member independently checks whether the accused's proof -/// failed verification from their perspective, and broadcasts this vote. +/// MUST byte-equal the literal passed to `EIP712(...)` in +/// `packages/enclave-contracts/contracts/slashing/SlashingManager.sol` +/// (`EIP712_DOMAIN_NAME` constant there). Off-chain signers and the on-chain +/// verifier share this one string — diverging here silently breaks +/// `ECDSA.recover` on every slashing submission. +pub const VOTE_DOMAIN_NAME: &str = "EnclaveSlashing"; + +/// EIP-712 domain `version` for accusation vote signatures. Same alignment +/// rule as [`VOTE_DOMAIN_NAME`]. +pub const VOTE_DOMAIN_VERSION: &str = "1"; + +/// EIP-712 struct typehash string for [`AccusationVote`]. +/// +/// MUST byte-equal `SlashingManager.VOTE_TYPEHASH`'s source string. Reordering +/// or renaming fields here without updating Solidity (or vice versa) silently +/// breaks signature recovery on chain. +pub const VOTE_TYPEHASH_STR: &str = + "AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bytes32 dataHash,uint256 deadline)"; + +/// Broadcast via gossip: a committee member's vote agreeing with an accusation. +/// +/// A node broadcasts an `AccusationVote` only when its own local verification +/// of the disputed proof also failed. There is no "disagree" vote on the +/// wire — a peer who finds the proof passes simply stays silent. This matches +/// the on-chain `SlashingManager._verifyAttestationEvidence`, which consumes +/// only agreeing signatures and treats every submitted vote as an +/// affirmative attestation; carrying an explicit `agrees` flag here would be +/// dead bytes off-chain and unverifiable gossip metadata (a malicious peer +/// could flip the flag in transit without invalidating the EIP-712 signature +/// over the on-chain digest). +/// +/// **Loss of fast-fail.** Off-chain quorum protocols sometimes track explicit +/// "no" votes so an accusation that clearly cannot reach quorum exits the +/// pending pool early. We trade that optimization for protocol simplicity and +/// soundness: an unanswerable accusation now runs to `vote_timeout` (default 5 min) +/// before being declared inconclusive. Other committee members' silence is the +/// signal; no separate signed message is required. #[derive(Message, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[rtype(result = "()")] pub struct AccusationVote { @@ -23,11 +58,13 @@ pub struct AccusationVote { pub accusation_id: [u8; 32], /// Ethereum address of the voter. pub voter: Address, - /// `true` if this node also saw the proof fail verification. - pub agrees: bool, /// keccak256 hash of the data as this node received it — for equivocation detection. pub data_hash: [u8; 32], - /// ECDSA signature of the voter over the vote fields. + /// Unix-seconds deadline shared across all voters for this accusation. + /// Bound into the EIP-712 vote digest and re-checked on-chain by + /// `SlashingManager._verifyAttestationEvidence` via `block.timestamp <= deadline`. + pub deadline: u64, + /// ECDSA signature of the voter over the EIP-712 vote digest. pub signature: ArcBytes, } @@ -35,8 +72,8 @@ impl Display for AccusationVote { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "AccusationVote {{ e3_id: {}, voter: {}, agrees: {} }}", - self.e3_id, self.voter, self.agrees + "AccusationVote {{ e3_id: {}, voter: {} }}", + self.e3_id, self.voter ) } } diff --git a/crates/events/src/enclave_event/commitment_consistency.rs b/crates/events/src/enclave_event/commitment_consistency.rs index 7cdcb21650..3ea2fa86d2 100644 --- a/crates/events/src/enclave_event/commitment_consistency.rs +++ b/crates/events/src/enclave_event/commitment_consistency.rs @@ -14,7 +14,7 @@ //! check proceed to ZK verification. use crate::{CorrelationId, E3id, ProofType, VerificationKind}; -use alloy::primitives::Address; +use alloy::primitives::{Address, Bytes}; use e3_utils::utility_types::ArcBytes; use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; @@ -27,11 +27,13 @@ use std::collections::BTreeSet; pub struct PartyProofData { pub party_id: u64, pub address: Address, - /// Each entry is a `(proof_type, public_signals, data_hash)` tuple from a - /// signed proof. The `data_hash` is `keccak256(abi.encode(proof.data, + /// Each entry is a `(proof_type, public_signals, data_hash, proof_data)` tuple + /// from a signed proof. The `data_hash` is `keccak256(abi.encode(proof.data, /// public_signals))` — used for the accusation protocol if a consistency - /// violation is detected. - pub proofs: Vec<(ProofType, ArcBytes, [u8; 32])>, + /// violation is detected. The `proof_data` (raw `proof.data` bytes) is forwarded + /// alongside so the on-chain slashing contract can recompute the dataHash from + /// the evidence preimage `abi.encode(proof_data, public_signals)`. + pub proofs: Vec<(ProofType, ArcBytes, [u8; 32], ArcBytes)>, } /// Published by [`ShareVerificationActor`] after ECDSA validation, before ZK. @@ -80,4 +82,9 @@ pub struct CommitmentConsistencyViolation { /// `keccak256(abi.encode(proof.data, public_signals))` of the accused party's /// proof — matches the data_hash used by the accusation protocol. pub data_hash: [u8; 32], + /// Raw `abi.encode(proof.data, public_signals)` — preimage of `data_hash`. + /// Forwarded to `SlashingManager.proposeSlash` so the on-chain contract can + /// verify `keccak256(evidence) == dataHash` bound in voter signatures. + #[serde(default)] + pub evidence: Bytes, } diff --git a/crates/events/src/enclave_event/committee_finalized.rs b/crates/events/src/enclave_event/committee_finalized.rs index 91b173613b..01f602952a 100644 --- a/crates/events/src/enclave_event/committee_finalized.rs +++ b/crates/events/src/enclave_event/committee_finalized.rs @@ -6,7 +6,6 @@ use crate::E3id; use actix::Message; -use alloy::primitives::U256; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display}; @@ -20,29 +19,22 @@ pub struct CommitteeFinalized { } impl CommitteeFinalized { - /// Sort committee members by ascending score so every node derives the same - /// deterministic ordering. The node with the lowest score ends up at index 0. - /// If scores are empty or unparseable, the order is left unchanged. + /// Sort committee members by ascending address so every node derives the same + /// deterministic ordering, matching the on-chain registry's canonical address-ascending + /// `topNodes` layout (see `_sortTopNodesByAscendingAddress` in `CiphernodeRegistryOwnable`). + /// The node with the numerically lowest address ends up at index 0 (= party 0). + /// Address comparison is done in lowercase to be independent of EIP-55 checksumming. pub fn sort_by_score(&mut self) { - if self.scores.len() != self.committee.len() || self.scores.is_empty() { - return; - } - - // Build (index, parsed_score) pairs let mut indices: Vec = (0..self.committee.len()).collect(); - let parsed: Vec> = - self.scores.iter().map(|s| s.parse::().ok()).collect(); - - // If any score fails to parse, leave order unchanged - if parsed.iter().any(|s| s.is_none()) { - return; - } - - indices.sort_by_key(|&i| parsed[i].unwrap()); + indices.sort_by_key(|&i| self.committee[i].to_lowercase()); let sorted_committee: Vec = indices.iter().map(|&i| self.committee[i].clone()).collect(); - let sorted_scores: Vec = indices.iter().map(|&i| self.scores[i].clone()).collect(); + let sorted_scores: Vec = if self.scores.len() == self.committee.len() { + indices.iter().map(|&i| self.scores[i].clone()).collect() + } else { + self.scores.clone() + }; self.committee = sorted_committee; self.scores = sorted_scores; diff --git a/crates/events/src/enclave_event/dkg_fold_attestation.rs b/crates/events/src/enclave_event/dkg_fold_attestation.rs new file mode 100644 index 0000000000..a6d94644f3 --- /dev/null +++ b/crates/events/src/enclave_event/dkg_fold_attestation.rs @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! ECDSA attestation binding a node's [`CircuitName::NodeFold`] output to its committee address. +//! +//! Used to close the attribution gap between `party_ids[i]` in the DKG aggregator and the +//! operator that produced fold row `i`: the aggregator cannot permute folds without valid +//! signatures from each claimed party. + +use crate::E3id; +use alloy::primitives::{keccak256, Address, U256}; +use alloy::signers::{local::PrivateKeySigner, SignerSync}; +use alloy::sol_types::SolValue; +use anyhow::{anyhow, Result}; +use derivative::Derivative; +use e3_utils::utility_types::ArcBytes; +use serde::{Deserialize, Serialize}; + +/// Commitments surfaced from a [`CircuitName::NodeFold`] proof (C4 aggregate outputs). +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct DkgFoldAggCommits { + pub sk_agg_commit: [u8; 32], + pub esm_agg_commit: [u8; 32], +} + +/// Canonical EIP-712 payload signed after `NodeDkgFold` completes. +/// +/// `chainId` and `verifying_contract` (the `DkgFoldAttestationVerifier`) are +/// part of the EIP-712 domain; `e3_id`, `party_id`, and the commitments are the +/// struct fields. Must stay aligned with `DkgFoldAttestationLib` in +/// `packages/enclave-contracts`. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct DkgFoldAttestationPayload { + pub e3_id: E3id, + /// Address of the on-chain `DkgFoldAttestationVerifier` (EIP-712 `verifyingContract`). + pub verifying_contract: Address, + /// Sortition / committee slot id (index into on-chain `topNodes` when ids are dense). + pub party_id: u64, + pub agg_commits: DkgFoldAggCommits, +} + +impl DkgFoldAttestationPayload { + /// `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. + pub fn domain_typehash() -> [u8; 32] { + keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)", + ) + .into() + } + + /// `keccak256("EnclaveDkgFoldAttestation")`. + pub fn domain_name_hash() -> [u8; 32] { + keccak256("EnclaveDkgFoldAttestation").into() + } + + /// `keccak256("1")`. + pub fn domain_version_hash() -> [u8; 32] { + keccak256("1").into() + } + + /// `keccak256("DkgFoldAttestation(uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)")`. + pub fn typehash() -> [u8; 32] { + keccak256( + "DkgFoldAttestation(uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)", + ) + .into() + } + + /// EIP-712 domain separator for this payload's chain + verifier. + pub fn domain_separator(&self) -> [u8; 32] { + let encoded = ( + Self::domain_typehash(), + Self::domain_name_hash(), + Self::domain_version_hash(), + U256::from(self.e3_id.chain_id()), + self.verifying_contract, + ) + .abi_encode(); + keccak256(&encoded).into() + } + + /// EIP-712 `hashStruct(DkgFoldAttestation)`. + pub fn struct_hash(&self) -> Result<[u8; 32]> { + let e3_id_u256: U256 = self + .e3_id + .clone() + .try_into() + .map_err(|_| anyhow!("E3id cannot be converted to U256"))?; + let encoded = ( + Self::typehash(), + e3_id_u256, + U256::from(self.party_id), + self.agg_commits.sk_agg_commit, + self.agg_commits.esm_agg_commit, + ) + .abi_encode(); + Ok(keccak256(&encoded).into()) + } + + /// EIP-712 typed-data hash: `keccak256("\x19\x01" || domainSeparator || structHash)`. + pub fn digest(&self) -> Result<[u8; 32]> { + let domain = self.domain_separator(); + let struct_hash = self.struct_hash()?; + let mut buf = Vec::with_capacity(2 + 32 + 32); + buf.push(0x19); + buf.push(0x01); + buf.extend_from_slice(&domain); + buf.extend_from_slice(&struct_hash); + Ok(keccak256(&buf).into()) + } +} + +/// EIP-712 typed-data signature over [`DkgFoldAttestationPayload::digest`]. +#[derive(Derivative, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derivative(Debug)] +pub struct SignedDkgFoldAttestation { + pub payload: DkgFoldAttestationPayload, + #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] + pub signature: ArcBytes, +} + +impl SignedDkgFoldAttestation { + pub fn sign(payload: DkgFoldAttestationPayload, signer: &PrivateKeySigner) -> Result { + let digest = payload.digest()?; + // `sign_hash_sync` signs the raw 32-byte hash without EIP-191 + // wrapping, which is what EIP-712 requires (`digest` is already + // the `\x19\x01 || domainSeparator || structHash` hash). + let sig = signer + .sign_hash_sync(&digest.into()) + .map_err(|e| anyhow!("Failed to sign DkgFoldAttestation: {e}"))?; + Ok(Self { + payload, + signature: ArcBytes::from_bytes(&sig.as_bytes()), + }) + } + + pub fn recover_address(&self) -> Result
{ + use alloy::primitives::Signature; + let sig = Signature::try_from(&self.signature[..]) + .map_err(|e| anyhow!("Invalid DkgFoldAttestation signature: {e}"))?; + let digest = self.payload.digest()?; + sig.recover_address_from_prehash(&digest.into()) + .map_err(|e| anyhow!("Failed to recover DkgFoldAttestation signer: {e}")) + } + + pub fn verify_signer(&self, expected: &Address) -> Result { + Ok(self.recover_address()? == *expected) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy::signers::local::PrivateKeySigner; + + #[test] + fn sign_and_recover_roundtrip() { + let signer: PrivateKeySigner = + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + .parse() + .unwrap(); + let payload = DkgFoldAttestationPayload { + e3_id: E3id::new("0", 1), + verifying_contract: Address::from([0x11u8; 20]), + party_id: 1, + agg_commits: DkgFoldAggCommits { + sk_agg_commit: [7u8; 32], + esm_agg_commit: [9u8; 32], + }, + }; + let signed = SignedDkgFoldAttestation::sign(payload, &signer).unwrap(); + assert_eq!(signed.recover_address().unwrap(), signer.address()); + } +} diff --git a/crates/events/src/enclave_event/dkg_recursive_aggregation_complete.rs b/crates/events/src/enclave_event/dkg_recursive_aggregation_complete.rs index 0458aabdbc..ddcd1220b2 100644 --- a/crates/events/src/enclave_event/dkg_recursive_aggregation_complete.rs +++ b/crates/events/src/enclave_event/dkg_recursive_aggregation_complete.rs @@ -10,7 +10,7 @@ //! [`PublicKeyAggregator`] collects these from all honest nodes for the //! cross-node aggregation phase. -use crate::{E3id, Proof}; +use crate::{E3id, Proof, SignedDkgFoldAttestation}; use serde::{Deserialize, Serialize}; /// NodeProofAggregator -> PublicKeyAggregator: fully aggregated DKG node proof. @@ -20,4 +20,23 @@ pub struct DKGRecursiveAggregationComplete { pub e3_id: E3id, pub party_id: u64, pub aggregated_proof: Option, + /// Binds the fold to the operator's registered address via `sk_agg` / `esm_agg` commits. + #[serde(default)] + pub fold_attestation: Option, +} + +impl DKGRecursiveAggregationComplete { + pub fn with_attestation( + e3_id: E3id, + party_id: u64, + aggregated_proof: Option, + fold_attestation: Option, + ) -> Self { + Self { + e3_id, + party_id, + aggregated_proof, + fold_attestation, + } + } } diff --git a/crates/events/src/enclave_event/mod.rs b/crates/events/src/enclave_event/mod.rs index 065517d96e..5437e9812d 100644 --- a/crates/events/src/enclave_event/mod.rs +++ b/crates/events/src/enclave_event/mod.rs @@ -25,6 +25,7 @@ mod decryption_share_proof_signed; mod decryption_share_proofs; mod decryptionshare_created; mod die; +mod dkg_fold_attestation; mod dkg_inner_proof_ready; mod dkg_recursive_aggregation_complete; mod e3_failed; @@ -91,6 +92,7 @@ pub use decryption_share_proof_signed::*; pub use decryption_share_proofs::*; pub use decryptionshare_created::*; pub use die::*; +pub use dkg_fold_attestation::*; pub use dkg_inner_proof_ready::*; pub use dkg_recursive_aggregation_complete::*; pub use e3_failed::*; diff --git a/crates/events/src/enclave_event/proof_failure_accusation.rs b/crates/events/src/enclave_event/proof_failure_accusation.rs index 185b5830e9..f965ccead6 100644 --- a/crates/events/src/enclave_event/proof_failure_accusation.rs +++ b/crates/events/src/enclave_event/proof_failure_accusation.rs @@ -33,6 +33,11 @@ pub struct ProofFailureAccusation { pub proof_type: ProofType, /// keccak256 hash of (data + proof) as received by the accuser. pub data_hash: [u8; 32], + /// Unix-seconds deadline after which votes signed for this accusation must be + /// rejected on-chain (`SlashingManager._verifyAttestationEvidence` enforces + /// `block.timestamp <= deadline`). Set by the accuser; every voter signs the + /// same value so the aggregated evidence carries one shared deadline. + pub deadline: u64, /// For C3a/C3b: the signed proof payload so other nodes can re-verify. /// `None` for proofs that all nodes already received. pub signed_payload: Option, diff --git a/crates/events/src/enclave_event/proof_verification_passed.rs b/crates/events/src/enclave_event/proof_verification_passed.rs index 15511f54e4..b28d1a0654 100644 --- a/crates/events/src/enclave_event/proof_verification_passed.rs +++ b/crates/events/src/enclave_event/proof_verification_passed.rs @@ -34,6 +34,13 @@ pub struct ProofVerificationPassed { pub data_hash: [u8; 32], /// Raw public signals from the verified proof — for commitment consistency checks. pub public_signals: ArcBytes, + /// Raw proof bytes — needed only by paths that may later turn this proof into + /// slashing evidence (so the on-chain contract can recompute and verify the + /// dataHash bound in voter signatures). Empty when the emitter did not have + /// the raw bytes available; downstream consumers that need it will fail + /// gracefully if so. + #[serde(default)] + pub proof_data: ArcBytes, } impl Display for ProofVerificationPassed { diff --git a/crates/events/src/enclave_event/publickey_aggregated.rs b/crates/events/src/enclave_event/publickey_aggregated.rs index 9c2d49ef8d..aa31923724 100644 --- a/crates/events/src/enclave_event/publickey_aggregated.rs +++ b/crates/events/src/enclave_event/publickey_aggregated.rs @@ -6,6 +6,7 @@ use crate::{E3id, OrderedSet, Proof}; use actix::Message; +use alloy::primitives::Address; use derivative::Derivative; use e3_utils::ArcBytes; use serde::{Deserialize, Serialize}; @@ -19,6 +20,9 @@ pub struct PublicKeyAggregated { pub pubkey: ArcBytes, // TODO: ArcBytes ? pub e3_id: E3id, pub nodes: OrderedSet, + /// Committee addresses in ascending `party_id` (score) order for hash binding. + #[serde(default)] + pub committee_addresses: Vec
, /// Hash-based aggregated PK commitment (last public signal of the C5 proof). /// Passed as `pkCommitment` to `publishCommittee`. pub pk_commitment: [u8; 32], @@ -26,6 +30,10 @@ pub struct PublicKeyAggregated { /// for on-chain verification. `None` when proof aggregation is disabled. #[serde(default)] pub dkg_aggregator_proof: Option, + /// ABI-encoded `(Attestation[], PartySlotBinding[])` for on-chain fold attestation verify. + /// Required when `dkg_aggregator_proof` is present; `None` otherwise. + #[serde(default)] + pub dkg_attestation_bundle: Option, } impl Display for PublicKeyAggregated { diff --git a/crates/evm/src/ciphernode_registry_sol.rs b/crates/evm/src/ciphernode_registry_sol.rs index 631f5e1618..95b542fd3d 100644 --- a/crates/evm/src/ciphernode_registry_sol.rs +++ b/crates/evm/src/ciphernode_registry_sol.rs @@ -22,8 +22,8 @@ use anyhow::Result; use e3_events::{ prelude::*, AggregatorChanged, BusHandle, CommitteeFinalizeRequested, CommitteeFinalized, E3RequestComplete, E3id, EType, EffectsEnabled, EnclaveEvent, EnclaveEventData, - EventSubscriber, EventType, OrderedSet, Proof, PublicKeyAggregated, Seed, Shutdown, - TicketGenerated, TicketId, + EventSubscriber, EventType, Proof, PublicKeyAggregated, Seed, Shutdown, TicketGenerated, + TicketId, }; use e3_utils::{ArcBytes, NotifySync, MAILBOX_LIMIT}; use std::collections::HashMap; @@ -495,6 +495,7 @@ impl Handler Handler, + dkg_attestation_bundle: Option<&[u8]>, ) -> Result { let e3_id_u256: U256 = e3_id.try_into()?; let public_key_bytes = Bytes::from(public_key.extract_bytes()); @@ -686,12 +689,17 @@ pub async fn publish_committee_to_registry encode_zk_proof(p)?, None => Bytes::new(), }; + let attestation_bundle: Bytes = match dkg_attestation_bundle { + Some(b) => Bytes::copy_from_slice(b), + None => Bytes::new(), + }; // RPC may not have synced finalization yet send_tx_with_retry("publishCommittee", &["CommitteeNotFinalized"], || { let provider = provider.clone(); let public_key_bytes = public_key_bytes.clone(); let proof = proof.clone(); + let attestation_bundle = attestation_bundle.clone(); async move { info!("Calling: contract.publishCommittee(..)"); let from_address = provider.provider().default_signer_address(); @@ -702,7 +710,13 @@ pub async fn publish_committee_to_registry( + provider: &P, + registry_address: Address, +) -> Result> { + sol! { + #[sol(rpc)] + interface ICiphernodeRegistryDkgFoldView { + function dkgFoldAttestationVerifier() external view returns (address); + } + } + + let contract = ICiphernodeRegistryDkgFoldView::new(registry_address, provider); + let verifier = contract.dkgFoldAttestationVerifier().call().await?; + if verifier == Address::ZERO { + Ok(None) + } else { + Ok(Some(verifier)) + } +} + +/// Read `CiphernodeRegistry.accusationVoteValidity()` — registry-wide off-chain +/// freshness window (seconds) accusers stamp on `AccusationVote.deadline`. +/// Returns the raw `uint256` as `U256`; callers decide how to clamp it to +/// their own arithmetic type. `Ok(None)` is reserved for the case where the +/// registry has been governance-disabled (`accusationVoteValidity = 0`) so +/// the caller can short-circuit without producing votes that will never +/// verify on chain. +pub async fn fetch_accusation_vote_validity( + provider: &P, + registry_address: Address, +) -> Result> { + sol! { + #[sol(rpc)] + interface ICiphernodeRegistryAccusationVoteView { + function accusationVoteValidity() external view returns (uint256); + } + } + + let contract = ICiphernodeRegistryAccusationVoteView::new(registry_address, provider); + let validity = contract.accusationVoteValidity().call().await?; + if validity.is_zero() { + Ok(None) + } else { + Ok(Some(validity)) + } +} + /// Wrapper for a reader and writer pub struct CiphernodeRegistrySol; diff --git a/crates/evm/src/dkg_attestation_bundle.rs b/crates/evm/src/dkg_attestation_bundle.rs new file mode 100644 index 0000000000..6a2adc0df6 --- /dev/null +++ b/crates/evm/src/dkg_attestation_bundle.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! ABI encoding for `publishCommittee`'s `dkgAttestationBundle` argument. +//! Must match `DkgFoldAttestationLib` structs in `packages/enclave-contracts`. + +use alloy::primitives::{Address, Bytes, B256, U256}; +use alloy::sol_types::SolValue; +use anyhow::{anyhow, Context as _, Result}; +use e3_events::SignedDkgFoldAttestation; +use std::collections::{BTreeSet, HashMap}; +use std::str::FromStr; + +alloy::sol! { + struct DkgFoldAttestationSol { + uint256 partyId; + bytes32 skAggCommit; + bytes32 esmAggCommit; + bytes signature; + } + + struct PartySlotBindingSol { + uint256 partyId; + address node; + } +} + +/// Build the bundle expected by `DkgFoldAttestationVerifier.verify`. +/// +/// `honest_party_ids` must be iterated in ascending order (e.g. a `BTreeSet`). +/// `bindings` are emitted in that order; `attestations` may be any order. +pub fn encode_dkg_attestation_bundle( + honest_party_ids: &BTreeSet, + party_nodes: &HashMap, + attestations: &HashMap, +) -> Result { + let mut binding_sols = Vec::with_capacity(honest_party_ids.len()); + let mut attestation_sols = Vec::with_capacity(honest_party_ids.len()); + + for party_id in honest_party_ids { + let node = party_nodes + .get(party_id) + .with_context(|| format!("missing party_nodes entry for party {party_id}"))?; + let att = attestations + .get(party_id) + .with_context(|| format!("missing fold attestation for party {party_id}"))?; + + if att.payload.party_id != *party_id { + return Err(anyhow!( + "attestation party_id {} does not match binding {}", + att.payload.party_id, + party_id + )); + } + + binding_sols.push(PartySlotBindingSol { + partyId: U256::from(*party_id), + node: Address::from_str(node) + .with_context(|| format!("invalid committee node address {node}"))?, + }); + + attestation_sols.push(DkgFoldAttestationSol { + partyId: U256::from(att.payload.party_id), + skAggCommit: B256::from(att.payload.agg_commits.sk_agg_commit), + esmAggCommit: B256::from(att.payload.agg_commits.esm_agg_commit), + signature: att.signature.extract_bytes().into(), + }); + } + + Ok(Bytes::from( + (attestation_sols, binding_sols).abi_encode_params(), + )) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy::primitives::keccak256; + use alloy::signers::local::PrivateKeySigner; + use e3_events::{DkgFoldAggCommits, DkgFoldAttestationPayload, E3id}; + + #[test] + fn roundtrip_abi_layout() { + let signer: PrivateKeySigner = + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + .parse() + .unwrap(); + let payload = DkgFoldAttestationPayload { + e3_id: E3id::new("31337", 1), + verifying_contract: Address::from([0x22u8; 20]), + party_id: 2, + agg_commits: DkgFoldAggCommits { + sk_agg_commit: [1u8; 32], + esm_agg_commit: [2u8; 32], + }, + }; + let signed = e3_events::SignedDkgFoldAttestation::sign(payload, &signer).unwrap(); + + let mut honest = BTreeSet::new(); + honest.insert(2); + let mut party_nodes = HashMap::new(); + party_nodes.insert(2, signer.address().to_string()); + let mut attestations = HashMap::new(); + attestations.insert(2, signed); + + let encoded = encode_dkg_attestation_bundle(&honest, &party_nodes, &attestations).unwrap(); + assert!(!encoded.is_empty()); + + let typehash = keccak256( + "DkgFoldAttestation(uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)", + ); + assert_eq!(typehash.len(), 32); + } +} diff --git a/crates/evm/src/evm_chain_gateway.rs b/crates/evm/src/evm_chain_gateway.rs index e192883a29..e361a2c009 100644 --- a/crates/evm/src/evm_chain_gateway.rs +++ b/crates/evm/src/evm_chain_gateway.rs @@ -246,8 +246,6 @@ impl Handler for EvmChainGateway { #[cfg(test)] mod tests { - use std::time::Duration; - use crate::EvmEvent; use super::*; diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index d30c1b9a4f..9332379eb5 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -6,6 +6,7 @@ mod bonding_registry_sol; mod ciphernode_registry_sol; +mod dkg_attestation_bundle; mod enclave_sol_reader; mod enclave_sol_writer; pub mod error_decoder; @@ -25,8 +26,10 @@ mod sync_start_extractor; pub use bonding_registry_sol::BondingRegistrySolReader; pub use ciphernode_registry_sol::{ - CiphernodeRegistrySol, CiphernodeRegistrySolReader, CiphernodeRegistrySolWriter, + fetch_accusation_vote_validity, fetch_dkg_fold_attestation_verifier, CiphernodeRegistrySol, + CiphernodeRegistrySolReader, CiphernodeRegistrySolWriter, }; +pub use dkg_attestation_bundle::encode_dkg_attestation_bundle; pub use enclave_sol_reader::EnclaveSolReader; pub use enclave_sol_writer::EnclaveSolWriter; pub use events::*; @@ -39,5 +42,5 @@ pub use fix_historical_order::*; pub use helpers::*; pub use repo::*; pub use slashing_manager_sol_reader::SlashingManagerSolReader; -pub use slashing_manager_sol_writer::SlashingManagerSolWriter; +pub use slashing_manager_sol_writer::{encode_attestation_evidence, SlashingManagerSolWriter}; pub use sync_start_extractor::*; diff --git a/crates/evm/src/slashing_manager_sol_writer.rs b/crates/evm/src/slashing_manager_sol_writer.rs index adf1ea3e83..1525e28aa7 100644 --- a/crates/evm/src/slashing_manager_sol_writer.rs +++ b/crates/evm/src/slashing_manager_sol_writer.rs @@ -4,8 +4,10 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -//! Subscribes to `AccusationQuorumReached` events and submits `proposeSlash` -//! transactions on the SlashingManager contract with committee attestation evidence. +//! Subscribes to `AccusationQuorumReached` events and submits committee-attested +//! slash proposals on the SlashingManager contract. Prefers party-attributed +//! `proposeSlashByDkgParty` when DKG anchors resolve, and falls back to +//! operator-attributed `proposeSlash` otherwise. use crate::error_decoder::format_evm_error; use crate::helpers::EthProvider; @@ -160,13 +162,15 @@ impl Handler { let decoded = format_evm_error(&err); - if rank > 0 { + let benign = decoded.contains("OperatorNotInCommittee") + || decoded.contains("VoterNotInCommittee") + || decoded.contains("DuplicateEvidence"); + if rank > 0 || benign { // Fallback submitters expect DuplicateEvidence reverts // when the primary submitter has already landed the tx. - warn!( - "Fallback submitter (rank {rank}): slash submission failed \ - (likely already submitted by primary): {decoded}" - ); + // Operator/VoterNotInCommittee indicate a stale off-chain accusation + // (e.g. cross-E3 race) — not a node-local fault. + warn!("Slash submission skipped (rank {rank}): {decoded}"); } else { bus.err( EType::Evm, @@ -191,25 +195,47 @@ impl Handler } /// Encode `AccusationQuorumReached` into the attestation evidence format expected -/// by `SlashingManager.proposeSlash()`: -/// `abi.encode(uint256 proofType, address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures)` +/// by both `SlashingManager.proposeSlash()` and `SlashingManager.proposeSlashByDkgParty()`: +/// `abi.encode(uint256 proofType, address[] voters, bytes32[] dataHashes, bytes evidence, uint256 deadline, bytes[] signatures)` /// -/// Voters are sorted ascending by address to satisfy the contract's duplicate-prevention check. -fn encode_attestation_evidence(data: &AccusationQuorumReached) -> Vec { +/// Voters are sorted ascending by address to satisfy the contract's duplicate-prevention +/// check. All `votes_for` share the same `deadline` (the accuser stamps one value at +/// accusation time and `AccusationManager::on_vote_received` rejects votes whose +/// deadline disagrees), so the encoder pulls it from the first vote. Returns `None` +/// if `votes_for` is empty — the on-chain submitter must skip the submission in that +/// case rather than send malformed calldata. +pub fn encode_attestation_evidence(data: &AccusationQuorumReached) -> Option> { + if data.votes_for.is_empty() || data.evidence.is_empty() { + return None; + } + // Collect and sort votes by voter address (ascending) let mut votes = data.votes_for.clone(); votes.sort_by_key(|v| v.voter); let proof_type = U256::from(data.proof_type as u8); let voters: Vec
= votes.iter().map(|v| v.voter).collect(); - let agrees: Vec = votes.iter().map(|v| v.agrees).collect(); let data_hashes: Vec<[u8; 32]> = votes.iter().map(|v| v.data_hash).collect(); + let evidence = Bytes::from(data.evidence.clone()); + // All voters signed the same deadline (enforced off-chain by AccusationManager); + // pick any one — the first vote suffices. + let deadline = U256::from(votes[0].deadline); let signatures: Vec = votes .iter() .map(|v| Bytes::from(v.signature.extract_bytes())) .collect(); - (proof_type, voters, agrees, data_hashes, signatures).abi_encode() + Some( + ( + proof_type, + voters, + data_hashes, + evidence, + deadline, + signatures, + ) + .abi_encode_params(), + ) } async fn submit_slash_proposal( @@ -220,12 +246,45 @@ async fn submit_slash_proposal( let e3_id: U256 = data.e3_id.clone().try_into()?; let operator = data.accused; - let proof_data = encode_attestation_evidence(&data); + // Empty `votes_for` only reaches this point if upstream invariants broke + // — `check_quorum` requires `len >= threshold_m >= 1` before emitting + // `AccusedFaulted`/`Equivocation`. Refuse to submit malformed calldata + // and surface a structured warning so an operator can debug the upstream + // gossip/quorum path rather than seeing a generic ABI-decode revert + // on chain. + let proof_data = match encode_attestation_evidence(&data) { + Some(bytes) => bytes, + None => { + warn!( + e3_id = %data.e3_id, + accused = %operator, + outcome = %data.outcome, + "Refusing to submit proposeSlash: AccusationQuorumReached has empty \ + votes_for or empty evidence preimage — submission dropped" + ); + return Err(anyhow::anyhow!( + "AccusationQuorumReached has empty votes_for or evidence; refused proposeSlash submission \ + (e3_id={}, accused={})", + data.e3_id, + operator + )); + } + }; + + let party_id = + resolve_party_id_for_operator(provider.clone(), contract_address, e3_id, operator) + .await + .ok() + .flatten(); send_tx_with_retry("proposeSlash", &[], || { - info!("proposeSlash() e3_id={:?} operator={:?}", e3_id, operator); + info!( + "proposeSlash() e3_id={:?} operator={:?} party_id={:?}", + e3_id, operator, party_id + ); let proof = Bytes::from(proof_data.clone()); let provider = provider.clone(); + let party_id = party_id; async move { let from_address = provider.provider().default_signer_address(); @@ -235,12 +294,62 @@ async fn submit_slash_proposal( .pending() .await?; let contract = ISlashingManager::new(contract_address, provider.provider()); - let builder = contract - .proposeSlash(e3_id, operator, proof) - .nonce(current_nonce); - let receipt = builder.send().await?.get_receipt().await?; + let receipt = if let Some(pid) = party_id { + contract + .proposeSlashByDkgParty(e3_id, pid, proof) + .nonce(current_nonce) + .send() + .await? + .get_receipt() + .await? + } else { + contract + .proposeSlash(e3_id, operator, proof) + .nonce(current_nonce) + .send() + .await? + .get_receipt() + .await? + }; Ok(receipt) } }) .await } + +async fn resolve_party_id_for_operator( + provider: EthProvider

, + contract_address: Address, + e3_id: U256, + operator: Address, +) -> Result> { + sol! { + #[sol(rpc)] + interface IRegistryDkgView { + function getDkgAnchors(uint256 e3Id) + external + view + returns (uint256[] memory partyIds, bytes32[] memory, bytes32[] memory); + function canonicalCommitteeNodeAt(uint256 e3Id, uint256 partyId) external view returns (address); + } + } + + let slashing = ISlashingManager::new(contract_address, provider.provider()); + let registry = slashing.ciphernodeRegistry().call().await?; + if registry == Address::ZERO { + return Ok(None); + } + + let registry_view = IRegistryDkgView::new(registry, provider.provider()); + let anchors = registry_view.getDkgAnchors(e3_id).call().await?; + for pid in anchors.partyIds { + let node = registry_view + .canonicalCommitteeNodeAt(e3_id, pid) + .call() + .await?; + if node == operator { + return Ok(Some(pid)); + } + } + Ok(None) +} diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index cab830ab09..de9057edb0 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -436,6 +436,8 @@ pub struct ThresholdKeyshare { /// the AggregatingDecryptionKey transition. Tuple is `(own_sk_share, own_esi_shares)`. /// Each entry is bincode-encoded `Vec>` of shape `[L][N]`. pending_own_dkg_shares: Option<(SensitiveBytes, Vec)>, + /// Set when C4 verification completes before `PkGenerationProofSigned` is applied. + pending_keyshare_publish: bool, } impl ThresholdKeyshare { @@ -452,8 +454,62 @@ impl ThresholdKeyshare { pending_share_decryption_data: None, pending_c4_verification_shares: None, pending_own_dkg_shares: None, + pending_keyshare_publish: false, } } + + fn store_signed_pk_generation_proof( + &mut self, + ec: &EventContext, + signed: SignedProofPayload, + ) -> Result<()> { + self.state.try_mutate(ec, |mut s| { + match &mut s.state { + KeyshareState::AggregatingDecryptionKey(adk) => { + adk.signed_pk_generation_proof = Some(signed.clone()); + } + KeyshareState::ReadyForDecryption(rfd) => { + rfd.signed_pk_generation_proof = Some(signed.clone()); + } + KeyshareState::Decrypting(d) => { + d.signed_pk_generation_proof = Some(signed.clone()); + } + other => { + warn!( + "PkGenerationProofSigned in {:?} — C1 proof not stored (unexpected state)", + other.variant_name() + ); + } + } + Ok(s) + }) + } + + fn keyshare_created_fields( + state: &KeyshareState, + ) -> Option<(&ArcBytes, &Option)> { + use KeyshareState as K; + match state { + K::ReadyForDecryption(s) => Some((&s.pk_share, &s.signed_pk_generation_proof)), + K::Decrypting(s) => Some((&s.pk_share, &s.signed_pk_generation_proof)), + _ => None, + } + } + + fn try_finish_deferred_keyshare_publish(&mut self, ec: EventContext) -> Result<()> { + if !self.pending_keyshare_publish { + return Ok(()); + } + let state = self.state.try_get()?; + let Some((_, signed)) = Self::keyshare_created_fields(&state.state) else { + return Ok(()); + }; + if signed.is_none() { + return Ok(()); + } + self.pending_keyshare_publish = false; + self.publish_keyshare_created(ec) + } } impl Actor for ThresholdKeyshare { @@ -738,16 +794,8 @@ impl ThresholdKeyshare { msg.party_id, msg.e3_id ); - // Store the signed proof in AggregatingDecryptionKey state - self.state.try_mutate(&ec, |s| { - let current: AggregatingDecryptionKey = s.clone().try_into()?; - s.new_state(KeyshareState::AggregatingDecryptionKey( - AggregatingDecryptionKey { - signed_pk_generation_proof: Some(msg.signed_proof), - ..current - }, - )) - })?; + self.store_signed_pk_generation_proof(&ec, msg.signed_proof)?; + self.try_finish_deferred_keyshare_publish(ec)?; Ok(()) } @@ -2202,17 +2250,37 @@ impl ThresholdKeyshare { let e3_id = state.get_e3_id(); let address = state.get_address().to_owned(); let party_id = state.get_party_id(); - let current: ReadyForDecryption = state.clone().try_into()?; + let Some((pk_share, signed_pk_generation_proof)) = + Self::keyshare_created_fields(&state.state) + else { + warn!( + "Deferring KeyshareCreated for party {} E3 {} — not in ReadyForDecryption/Decrypting ({})", + party_id, + e3_id, + state.state.variant_name() + ); + self.pending_keyshare_publish = true; + return Ok(()); + }; + + if signed_pk_generation_proof.is_none() { + warn!( + "Deferring KeyshareCreated for party {} E3 {} — C1 proof not stored yet (PkGenerationProofSigned race)", + party_id, e3_id + ); + self.pending_keyshare_publish = true; + return Ok(()); + } info!("Publishing Exchange #4 (KeyshareCreated) for E3 {}", e3_id); self.bus.publish( KeyshareCreated { - pubkey: current.pk_share, + pubkey: pk_share.clone(), e3_id: e3_id.clone(), node: address, party_id, - signed_pk_generation_proof: current.signed_pk_generation_proof, + signed_pk_generation_proof: signed_pk_generation_proof.clone(), }, ec, )?; @@ -2732,6 +2800,7 @@ impl Handler for ThresholdKeyshare { self.pending_shares.clear(); self.pending_share_decryption_data = None; self.pending_c4_verification_shares = None; + self.pending_keyshare_publish = false; self.notify_sync(ctx, Die); } } diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 4bcbff4372..43d24bbbef 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -71,8 +71,8 @@ use e3_zk_helpers::threshold::pk_aggregation::PkAggregationCircuitData; use e3_zk_helpers::CiphernodesCommittee; use e3_zk_prover::{ prove_decryption_aggregation_jobs, prove_dkg_aggregation, prove_node_dkg_fold, CircuitVariant, - DecryptionAggregationJob, DkgAggregationInput, NodeDkgFoldInput, Provable, ZkBackend, ZkError, - ZkProver, + DecryptionAggregationJob, DkgAggregationInput, NodeDkgFoldInput, NodeDkgFoldProveResult, + Provable, ZkBackend, ZkError, ZkProver, }; use fhe::bfv::{Ciphertext, Encoding, Plaintext, PublicKey, SecretKey}; use fhe::mbfv::PublicKeyShare; @@ -246,9 +246,10 @@ async fn handle_compute_request_event( let (msg, ctx) = msg.into_components(); let request_snapshot = msg.clone(); + let report_for_worker = report.clone(); let pool_result = pool .spawn(job_name, TaskTimeouts::default(), move || { - handle_compute_request(rng, cipher, zk_prover, msg) + handle_compute_request(rng, cipher, zk_prover, msg, report_for_worker) }) .await; @@ -512,6 +513,7 @@ fn handle_compute_request( cipher: Arc, zk_prover: Option>, request: ComputeRequest, + report: Option>, ) -> (Result, Duration) { let id: u8 = rand::thread_rng().gen(); @@ -519,7 +521,9 @@ fn handle_compute_request( ComputeRequestKind::TrBFV(trbfv_req) => { handle_trbfv_request(rng, cipher, trbfv_req, request, id) } - ComputeRequestKind::Zk(zk_req) => handle_zk_request(cipher, zk_prover, zk_req, request, id), + ComputeRequestKind::Zk(zk_req) => { + handle_zk_request(cipher, zk_prover, zk_req, request, id, report) + } } } @@ -531,27 +535,23 @@ fn handle_trbfv_request( id: u8, ) -> (Result, Duration) { match trbfv_req { - TrBFVRequest::GenPkShareAndSkSss(req) => { - timefunc( - "gen_pk_share_and_sk_sss", - id, - || match gen_pk_share_and_sk_sss(&rng, &cipher, req) { - Ok(o) => Ok(ComputeResponse::trbfv( - TrBFVResponse::GenPkShareAndSkSss(o), - request.correlation_id, - request.e3_id, - )), - Err(e) => Err(ComputeRequestError::new( - ComputeRequestErrorKind::TrBFV(TrBFVError::GenPkShareAndSkSss( - e.to_string(), - )), - request, - )), - }, - ) - } + TrBFVRequest::GenPkShareAndSkSss(req) => timefunc("gen_pk_share_and_sk_sss", id, || { + let mut rng_guard = rng.lock().expect("SharedRng mutex poisoned"); + match gen_pk_share_and_sk_sss(&mut *rng_guard, &cipher, req) { + Ok(o) => Ok(ComputeResponse::trbfv( + TrBFVResponse::GenPkShareAndSkSss(o), + request.correlation_id, + request.e3_id, + )), + Err(e) => Err(ComputeRequestError::new( + ComputeRequestErrorKind::TrBFV(TrBFVError::GenPkShareAndSkSss(e.to_string())), + request, + )), + } + }), TrBFVRequest::GenEsiSss(req) => timefunc("gen_esi_sss", id, || { - match gen_esi_sss(&rng, &cipher, req) { + let mut rng_guard = rng.lock().expect("SharedRng mutex poisoned"); + match gen_esi_sss(&mut *rng_guard, &cipher, req) { Ok(o) => Ok(ComputeResponse::trbfv( TrBFVResponse::GenEsiSss(o), request.correlation_id, @@ -626,6 +626,7 @@ fn handle_zk_request( zk_req: ZkRequest, request: ComputeRequest, id: u8, + report: Option>, ) -> (Result, Duration) { let Some(prover) = zk_prover else { return ( @@ -677,7 +678,7 @@ fn handle_zk_request( }) } ZkRequest::NodeDkgFold(req) => timefunc("zk_node_dkg_fold", id, || { - handle_node_dkg_fold_proof(&prover, req, request.clone()) + handle_node_dkg_fold_proof(&prover, req, request.clone(), report.clone()) }), ZkRequest::DkgAggregation(req) => timefunc("zk_dkg_aggregation", id, || { handle_dkg_aggregation_proof(&prover, req, request.clone()) @@ -692,6 +693,7 @@ fn handle_node_dkg_fold_proof( prover: &ZkProver, req: NodeDkgFoldRequest, request: ComputeRequest, + report: Option>, ) -> Result { let artifacts_dir = req.params_preset.artifacts_dir(); let job_id = zk_bb_work_id(&request); @@ -709,12 +711,23 @@ fn handle_node_dkg_fold_proof( c4b_proof: &req.c4b_proof, party_id: req.party_id, }; - let proof = prove_node_dkg_fold(prover, &input, &job_id, &artifacts_dir).map_err(|e| { + let NodeDkgFoldProveResult { + proof, + step_timings, + } = prove_node_dkg_fold(prover, &input, &job_id, &artifacts_dir).map_err(|e| { ComputeRequestError::new( ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), request.clone(), ) })?; + if let Some(report) = report { + for step in step_timings { + report.do_send(TrackDuration::new( + format!("NodeDkgFold/{}", step.step), + Duration::from_secs_f64(step.seconds), + )); + } + } Ok(ComputeResponse::zk( ZkResponse::NodeDkgFold(NodeDkgFoldResponse { proof }), request.correlation_id, diff --git a/crates/test-helpers/src/lib.rs b/crates/test-helpers/src/lib.rs index 522ebda661..aa552deeb0 100644 --- a/crates/test-helpers/src/lib.rs +++ b/crates/test-helpers/src/lib.rs @@ -99,6 +99,15 @@ pub fn create_shared_rng_from_u64(value: u64) -> Arc1` +/// can run `GenPkShare` / `GenEsiSss` in parallel. +pub fn derive_shared_rng(base_seed: u64, salt: u64) -> SharedRng { + create_shared_rng_from_u64(base_seed.wrapping_add(salt)) +} + pub fn create_seed_from_u64(value: u64) -> Seed { Seed(ChaCha20Rng::seed_from_u64(value).get_seed()) } diff --git a/crates/test-helpers/src/usecase_helpers.rs b/crates/test-helpers/src/usecase_helpers.rs index 467d126963..f844838eb8 100644 --- a/crates/test-helpers/src/usecase_helpers.rs +++ b/crates/test-helpers/src/usecase_helpers.rs @@ -65,25 +65,31 @@ pub fn generate_shares_hash_map( pk_share, e_sm_raw, .. - } = gen_pk_share_and_sk_sss( - &rng, - &cipher, - GenPkShareAndSkSssRequest { - trbfv_config: trbfv_config.clone(), - crp: ArcBytes::from_bytes(&crp.to_bytes()), - lambda: 40, - num_ciphertexts: 1, - }, - )?; - - let GenEsiSssResponse { esi_sss } = gen_esi_sss( - &rng, - &cipher, - GenEsiSssRequest { - trbfv_config: trbfv_config.clone(), - e_sm_raw: e_sm_raw.clone(), - }, - )?; + } = { + let mut rng_guard = rng.lock().unwrap(); + gen_pk_share_and_sk_sss( + &mut *rng_guard, + &cipher, + GenPkShareAndSkSssRequest { + trbfv_config: trbfv_config.clone(), + crp: ArcBytes::from_bytes(&crp.to_bytes()), + lambda: 40, + num_ciphertexts: 1, + }, + ) + }?; + + let GenEsiSssResponse { esi_sss } = { + let mut rng_guard = rng.lock().unwrap(); + gen_esi_sss( + &mut *rng_guard, + &cipher, + GenEsiSssRequest { + trbfv_config: trbfv_config.clone(), + e_sm_raw: e_sm_raw.clone(), + }, + )? + }; // Decrypt locally stored secrets let decrypted_sk_sss: SharedSecret = sk_sss.decrypt(&cipher)?; diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 1378265e9f..85c7bdf7ad 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -7,17 +7,18 @@ use actix::Actor; use alloy::primitives::{Address, FixedBytes, I256, U256}; use alloy::signers::local::PrivateKeySigner; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use e3_bfv_client::decode_bytes_to_vec_u64; use e3_ciphernode_builder::{CiphernodeBuilder, EventSystem}; use e3_config::BBPath; use e3_crypto::Cipher; use e3_events::{ - prelude::*, BusHandle, CiphertextOutputPublished, CommitteeFinalized, ComputeRequestKind, - ComputeResponseKind, ConfigurationUpdated, E3Requested, E3id, EffectsEnabled, EnclaveEvent, - EnclaveEventData, EventType, GetEvents, HistoryCollector, OperatorActivationChanged, - OrderedSet, PkAggregationProofPending, PkAggregationProofRequest, PlaintextAggregated, - ProofType, Seed, TakeEvents, TicketBalanceUpdated, VerificationKind, ZkRequest, ZkResponse, + hlc::HlcTimestamp, prelude::*, BusHandle, CiphertextOutputPublished, CommitteeFinalized, + ComputeRequestKind, ComputeResponseKind, ConfigurationUpdated, E3Requested, E3id, + EffectsEnabled, EnclaveEvent, EnclaveEventData, EventType, GetEvents, HistoryCollector, + OperatorActivationChanged, OrderedSet, PkAggregationProofPending, PkAggregationProofRequest, + PlaintextAggregated, ProofType, Seed, TakeEvents, TicketBalanceUpdated, VerificationKind, + ZkRequest, ZkResponse, }; use e3_fhe_params::DEFAULT_BFV_PRESET; use e3_fhe_params::{build_pair_for_preset, create_deterministic_crp_from_default_seed}; @@ -31,7 +32,7 @@ use e3_test_helpers::ciphernode_system::{ CiphernodeHistory, CiphernodeSystem, CiphernodeSystemBuilder, }; use e3_test_helpers::{ - create_seed_from_u64, create_shared_rng_from_u64, find_bb, with_tracing, AddToCommittee, + create_seed_from_u64, derive_shared_rng, find_bb, with_tracing, AddToCommittee, }; use e3_trbfv::helpers::calculate_error_size; use e3_trbfv::{TrBFVRequest, TrBFVResponse}; @@ -49,6 +50,7 @@ use rand::rngs::OsRng; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; use std::ffi::OsString; +use std::sync::atomic::{AtomicU64, Ordering}; use std::time::{Duration, Instant}; use std::{fs, path::PathBuf, sync::Arc}; use tokio::{ @@ -118,6 +120,57 @@ fn select_benchmark_params() -> BenchmarkParams { } } +/// Whether `test_trbfv_actor` runs the full recursive fold + aggregator path (default: on). +/// +/// Set `BENCHMARK_PROOF_AGGREGATION=false` for a baseline run without node folds / folded Π_DKG. +fn benchmark_proof_aggregation_enabled() -> bool { + match std::env::var("BENCHMARK_PROOF_AGGREGATION") + .unwrap_or_else(|_| "true".into()) + .to_ascii_lowercase() + .as_str() + { + "0" | "false" | "no" | "off" => false, + _ => true, + } +} + +/// Rayon multithread pool concurrency for benchmark runs (`BENCHMARK_MULTITHREAD_JOBS`, default 1). +fn benchmark_multithread_concurrent_jobs() -> usize { + std::env::var("BENCHMARK_MULTITHREAD_JOBS") + .ok() + .and_then(|s| s.parse().ok()) + .filter(|&n| n >= 1) + .unwrap_or(1) +} + +static NEXT_BENCHMARK_NODE_RNG_SALT: AtomicU64 = AtomicU64::new(1); + +/// One ChaCha20 mutex per ciphernode in `test_trbfv_actor` (see `derive_shared_rng`). +fn next_benchmark_node_rng(base_seed: u64) -> e3_utils::SharedRng { + let salt = NEXT_BENCHMARK_NODE_RNG_SALT.fetch_add(1, Ordering::Relaxed); + derive_shared_rng(base_seed, salt) +} + +/// Fold attestation verifier address for benchmark JSON reports (env override or default). +fn benchmark_dkg_fold_attestation_verifier_address() -> Option

{ + if !benchmark_proof_aggregation_enabled() { + return None; + } + std::env::var("BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER") + .ok() + .and_then(|s| s.parse().ok()) + .or_else(|| "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0".parse().ok()) +} + +/// Slashing manager address for benchmarks (no live RPC; used as EIP-712 +/// `verifyingContract` for accusation vote signatures). +fn benchmark_slashing_manager_address() -> Address { + let addr = std::env::var("BENCHMARK_SLASHING_MANAGER") + .unwrap_or_else(|_| "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707".to_string()); + addr.parse() + .expect("BENCHMARK_SLASHING_MANAGER must be a valid address") +} + /// RAII guard that restores the benchmark-specific collector-timeout env vars on scope exit. /// This prevents leaking secure-mode tuning into other tests/processes. struct EnvTimeoutVarsGuard { @@ -155,6 +208,53 @@ impl Drop for EnvTimeoutVarsGuard { } } +/// RAII guard that restores a single env var on scope exit. +struct ScopedEnvVar { + name: &'static str, + original: Option, +} + +impl ScopedEnvVar { + fn set(name: &'static str, value: &str) -> Self { + let original = std::env::var_os(name); + std::env::set_var(name, value); + Self { name, original } + } +} + +impl Drop for ScopedEnvVar { + fn drop(&mut self) { + if let Some(v) = &self.original { + std::env::set_var(self.name, v); + } else { + std::env::remove_var(self.name); + } + } +} + +async fn copy_dir_recursive(src: &std::path::Path, dst: &std::path::Path) -> Result<()> { + tokio::fs::create_dir_all(dst).await?; + let mut entries = tokio::fs::read_dir(src).await?; + while let Some(entry) = entries.next_entry().await? { + let file_type = entry.file_type().await?; + let dest = dst.join(entry.file_name()); + if file_type.is_dir() { + Box::pin(copy_dir_recursive(&entry.path(), &dest)).await?; + } else { + tokio::fs::copy(entry.path(), &dest) + .await + .with_context(|| { + format!( + "copy circuit artifact {} -> {}", + entry.path().display(), + dest.display() + ) + })?; + } + } + Ok(()) +} + /// Create a ZkBackend for integration tests. /// If a local bb binary is found, uses it with fixture files (fast path). /// Otherwise, calls `ensure_installed()` to download bb + circuits (CI path). @@ -167,6 +267,10 @@ async fn setup_test_zk_backend( let bb_binary = noir_dir.join("bin").join("bb"); let circuits_dir = noir_dir.join("circuits"); let work_dir = noir_dir.join("work").join("test_node"); + let repo_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("..") + .join(".."); + let dist_preset = repo_root.join("dist/circuits").join(preset_subdir); if let Some(bb) = find_bb().await { tokio::fs::create_dir_all(bb_binary.parent().unwrap()) @@ -180,335 +284,371 @@ async fn setup_test_zk_backend( #[cfg(not(unix))] compile_error!("Integration tests require unix symlink support"); - let circuits_build_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("circuits") - .join("bin"); - let dkg_target = circuits_build_root.join("dkg").join("target"); - let threshold_target = circuits_build_root.join("threshold").join("target"); - let c3_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("c3_fold") - .join("target"); - let c3_fold_kernel_target = circuits_build_root - .join("recursive_aggregation") - .join("c3_fold_kernel") - .join("target"); - let c6_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("c6_fold") - .join("target"); - let c6_fold_kernel_target = circuits_build_root - .join("recursive_aggregation") - .join("c6_fold_kernel") - .join("target"); - let c2ab_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("c2ab_fold") - .join("target"); - let c3ab_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("c3ab_fold") - .join("target"); - let c4ab_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("c4ab_fold") - .join("target"); - let node_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("node_fold") - .join("target"); - let nodes_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("nodes_fold") - .join("target"); - let nodes_fold_kernel_target = circuits_build_root - .join("recursive_aggregation") - .join("nodes_fold_kernel") - .join("target"); - let dkg_aggregator_target = circuits_build_root - .join("recursive_aggregation") - .join("dkg_aggregator") - .join("target"); - let decryption_aggregator_target = circuits_build_root - .join("recursive_aggregation") - .join("decryption_aggregator") - .join("target"); - - // Helper: copy {name}.json + VK artifacts into a destination directory. - // vk_suffix/vk_hash_suffix select the source VK flavor: - // ".vk_noir" / ".vk_noir_hash" → Recursive variant (inner proofs) - // ".vk_recursive" / ".vk_recursive_hash" → Default variant (wrapper/fold proofs) - // ".vk" / ".vk_hash" → EVM variant - async fn copy_circuit( - src_dir: &std::path::Path, - dst_dir: &std::path::Path, - name: &str, - vk_suffix: &str, - vk_hash_suffix: &str, - ) { - tokio::fs::create_dir_all(dst_dir).await.unwrap(); - tokio::fs::copy( - src_dir.join(format!("{name}.json")), - dst_dir.join(format!("{name}.json")), + let preset_out = circuits_dir.join(preset_subdir); + let dist_marker = dist_preset.join("recursive/dkg/pk/pk.json"); + let circuits_bin_marker = repo_root.join("circuits/bin/dkg/target/pk.json"); + // `circuits/bin` is preset-agnostic on disk — only `dist/circuits//.build-stamp.json` + // (written by `pnpm build:circuits --preset `) records which preset the + // most recent local build targeted. Without it we cannot tell whether `circuits/bin` + // matches `preset_subdir`, and copying the wrong preset's artifacts would silently + // produce invalid proofs. + let preset_build_stamp = dist_preset.join(".build-stamp.json"); + + if dist_marker.exists() { + copy_dir_recursive(&dist_preset, &preset_out).await?; + } else if !circuits_bin_marker.exists() || !preset_build_stamp.exists() { + // Either no local build exists, or the local build cannot be proven to match + // the requested preset; download the pinned release tarball instead. + println!( + "No verifiable local circuit fixtures for preset `{}` \ + (need either dist/circuits/{}/recursive/dkg/pk/pk.json \ + or circuits/bin + dist/circuits/{}/.build-stamp.json); \ + downloading release circuits via ensure_installed()...", + preset_subdir, preset_subdir, preset_subdir + ); + let backend = ZkBackend::new(BBPath::Default(bb_binary), circuits_dir, work_dir); + backend + .ensure_installed() + .await + .context("download ZK circuits for integration tests")?; + return Ok((backend, temp)); + } else { + let circuits_build_root = repo_root.join("circuits").join("bin"); + let dkg_target = circuits_build_root.join("dkg").join("target"); + let threshold_target = circuits_build_root.join("threshold").join("target"); + let c3_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c3_fold") + .join("target"); + let c3_fold_kernel_target = circuits_build_root + .join("recursive_aggregation") + .join("c3_fold_kernel") + .join("target"); + let c6_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c6_fold") + .join("target"); + let c6_fold_kernel_target = circuits_build_root + .join("recursive_aggregation") + .join("c6_fold_kernel") + .join("target"); + let c2ab_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c2ab_fold") + .join("target"); + let c3ab_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c3ab_fold") + .join("target"); + let c4ab_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c4ab_fold") + .join("target"); + let node_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("node_fold") + .join("target"); + let nodes_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("nodes_fold") + .join("target"); + let nodes_fold_kernel_target = circuits_build_root + .join("recursive_aggregation") + .join("nodes_fold_kernel") + .join("target"); + let dkg_aggregator_target = circuits_build_root + .join("recursive_aggregation") + .join("dkg_aggregator") + .join("target"); + let decryption_aggregator_target = circuits_build_root + .join("recursive_aggregation") + .join("decryption_aggregator") + .join("target"); + + // Helper: copy {name}.json + VK artifacts into a destination directory. + // vk_suffix/vk_hash_suffix select the source VK flavor: + // ".vk_noir" / ".vk_noir_hash" → Recursive variant (inner proofs) + // ".vk_recursive" / ".vk_recursive_hash" → Default variant (wrapper/fold proofs) + // ".vk" / ".vk_hash" → EVM variant + async fn copy_circuit( + src_dir: &std::path::Path, + dst_dir: &std::path::Path, + name: &str, + vk_suffix: &str, + vk_hash_suffix: &str, + ) -> Result<()> { + tokio::fs::create_dir_all(dst_dir).await?; + let copy_file = |src: std::path::PathBuf, dst: std::path::PathBuf| async move { + tokio::fs::copy(&src, &dst).await.with_context(|| { + format!( + "copy circuit artifact {} -> {}", + src.display(), + dst.display() + ) + }) + }; + copy_file( + src_dir.join(format!("{name}.json")), + dst_dir.join(format!("{name}.json")), + ) + .await?; + copy_file( + src_dir.join(format!("{name}{vk_suffix}")), + dst_dir.join(format!("{name}.vk")), + ) + .await?; + let vk_hash_src = src_dir.join(format!("{name}{vk_hash_suffix}")); + let vk_hash_src = if tokio::fs::try_exists(&vk_hash_src).await? { + vk_hash_src + } else { + // `bb write_vk` leaves `vk_hash` in some aggregation target dirs. + src_dir.join("vk_hash") + }; + copy_file(vk_hash_src, dst_dir.join(format!("{name}.vk_hash"))).await?; + Ok(()) + } + + // ── recursive/ variant (inner/base proofs, uses .vk_noir) ────────── + let preset_dir = circuits_dir.join(preset_subdir); + + let rv = preset_dir.join("recursive"); + + // T0 (pk) + copy_circuit( + &dkg_target, + &rv.join("dkg/pk"), + "pk", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - tokio::fs::copy( - src_dir.join(format!("{name}{vk_suffix}")), - dst_dir.join(format!("{name}.vk")), + .await?; + // C1 (pk_generation) + copy_circuit( + &threshold_target, + &rv.join("threshold/pk_generation"), + "pk_generation", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - tokio::fs::copy( - src_dir.join(format!("{name}{vk_hash_suffix}")), - dst_dir.join(format!("{name}.vk_hash")), + .await?; + // C2a (sk_share_computation) + copy_circuit( + &dkg_target, + &rv.join("dkg/sk_share_computation"), + "sk_share_computation", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - } - - // ── recursive/ variant (inner/base proofs, uses .vk_noir) ────────── - let preset_dir = circuits_dir.join(preset_subdir); - - let rv = preset_dir.join("recursive"); + .await?; + // C2b (e_sm_share_computation) + copy_circuit( + &dkg_target, + &rv.join("dkg/e_sm_share_computation"), + "e_sm_share_computation", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; + // C3 (share_encryption) + copy_circuit( + &dkg_target, + &rv.join("dkg/share_encryption"), + "share_encryption", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; + // C4 (dkg/share_decryption) + copy_circuit( + &dkg_target, + &rv.join("dkg/share_decryption"), + "share_decryption", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; + // C5 (pk_aggregation) + copy_circuit( + &threshold_target, + &rv.join("threshold/pk_aggregation"), + "pk_aggregation", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; + // C6 (threshold/share_decryption) + copy_circuit( + &threshold_target, + &rv.join("threshold/share_decryption"), + "share_decryption", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; + // C7 (decrypted_shares_aggregation) + copy_circuit( + &threshold_target, + &rv.join("threshold/decrypted_shares_aggregation"), + "decrypted_shares_aggregation", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; - // T0 (pk) - copy_circuit( - &dkg_target, - &rv.join("dkg/pk"), - "pk", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C1 (pk_generation) - copy_circuit( - &threshold_target, - &rv.join("threshold/pk_generation"), - "pk_generation", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C2a (sk_share_computation) - copy_circuit( - &dkg_target, - &rv.join("dkg/sk_share_computation"), - "sk_share_computation", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C2b (e_sm_share_computation) - copy_circuit( - &dkg_target, - &rv.join("dkg/e_sm_share_computation"), - "e_sm_share_computation", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C3 (share_encryption) - copy_circuit( - &dkg_target, - &rv.join("dkg/share_encryption"), - "share_encryption", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C4 (dkg/share_decryption) - copy_circuit( - &dkg_target, - &rv.join("dkg/share_decryption"), - "share_decryption", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C5 (pk_aggregation) - copy_circuit( - &threshold_target, - &rv.join("threshold/pk_aggregation"), - "pk_aggregation", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C6 (threshold/share_decryption) - copy_circuit( - &threshold_target, - &rv.join("threshold/share_decryption"), - "share_decryption", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C7 (decrypted_shares_aggregation) - copy_circuit( - &threshold_target, - &rv.join("threshold/decrypted_shares_aggregation"), - "decrypted_shares_aggregation", - ".vk_noir", - ".vk_noir_hash", - ) - .await; + // ── default/ variant (recursive aggregation bins, uses .vk_recursive) ─── - // ── default/ variant (recursive aggregation bins, uses .vk_recursive) ─── + let dv = preset_dir.join("default"); - let dv = preset_dir.join("default"); + // C5 (pk_aggregation) — proven with noir-recursive-no-zk and folded into + // DkgAggregator, so it must be staged under default/ too. + copy_circuit( + &threshold_target, + &dv.join("threshold/pk_aggregation"), + "pk_aggregation", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; - // C5 (pk_aggregation) — proven with noir-recursive-no-zk and folded into - // DkgAggregator, so it must be staged under default/ too. - copy_circuit( - &threshold_target, - &dv.join("threshold/pk_aggregation"), - "pk_aggregation", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - - copy_circuit( - &c3_fold_target, - &dv.join("recursive_aggregation/c3_fold"), - "c3_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &c3_fold_kernel_target, - &dv.join("recursive_aggregation/c3_fold_kernel"), - "c3_fold_kernel", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &c6_fold_target, - &dv.join("recursive_aggregation/c6_fold"), - "c6_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &c6_fold_kernel_target, - &dv.join("recursive_aggregation/c6_fold_kernel"), - "c6_fold_kernel", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &c2ab_fold_target, - &dv.join("recursive_aggregation/c2ab_fold"), - "c2ab_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &c3ab_fold_target, - &dv.join("recursive_aggregation/c3ab_fold"), - "c3ab_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &c4ab_fold_target, - &dv.join("recursive_aggregation/c4ab_fold"), - "c4ab_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &node_fold_target, - &dv.join("recursive_aggregation/node_fold"), - "node_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &nodes_fold_target, - &dv.join("recursive_aggregation/nodes_fold"), - "nodes_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &nodes_fold_kernel_target, - &dv.join("recursive_aggregation/nodes_fold_kernel"), - "nodes_fold_kernel", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &dkg_aggregator_target, - &dv.join("recursive_aggregation/dkg_aggregator"), - "dkg_aggregator", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &decryption_aggregator_target, - &dv.join("recursive_aggregation/decryption_aggregator"), - "decryption_aggregator", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - // C7 (decrypted_shares_aggregation) — proven with noir-recursive-no-zk and - // folded into DecryptionAggregator, so it must also be staged under default/. - copy_circuit( - &threshold_target, - &dv.join("threshold/decrypted_shares_aggregation"), - "decrypted_shares_aggregation", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; + copy_circuit( + &c3_fold_target, + &dv.join("recursive_aggregation/c3_fold"), + "c3_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &c3_fold_kernel_target, + &dv.join("recursive_aggregation/c3_fold_kernel"), + "c3_fold_kernel", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &c6_fold_target, + &dv.join("recursive_aggregation/c6_fold"), + "c6_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &c6_fold_kernel_target, + &dv.join("recursive_aggregation/c6_fold_kernel"), + "c6_fold_kernel", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &c2ab_fold_target, + &dv.join("recursive_aggregation/c2ab_fold"), + "c2ab_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &c3ab_fold_target, + &dv.join("recursive_aggregation/c3ab_fold"), + "c3ab_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &c4ab_fold_target, + &dv.join("recursive_aggregation/c4ab_fold"), + "c4ab_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &node_fold_target, + &dv.join("recursive_aggregation/node_fold"), + "node_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &nodes_fold_target, + &dv.join("recursive_aggregation/nodes_fold"), + "nodes_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &nodes_fold_kernel_target, + &dv.join("recursive_aggregation/nodes_fold_kernel"), + "nodes_fold_kernel", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &dkg_aggregator_target, + &dv.join("recursive_aggregation/dkg_aggregator"), + "dkg_aggregator", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &decryption_aggregator_target, + &dv.join("recursive_aggregation/decryption_aggregator"), + "decryption_aggregator", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + // C7 (decrypted_shares_aggregation) — proven with noir-recursive-no-zk and + // folded into DecryptionAggregator, so it must also be staged under default/. + copy_circuit( + &threshold_target, + &dv.join("threshold/decrypted_shares_aggregation"), + "decrypted_shares_aggregation", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; - // ── evm/ variant (on-chain verification: DKG aggregator, C7) ─────────── + // ── evm/ variant (on-chain verification: DKG aggregator, C7) ─────────── - let ev = preset_dir.join("evm"); + let ev = preset_dir.join("evm"); - // DKG aggregator — EVM-targeted (folds + C5 verified inside) - copy_circuit( - &dkg_aggregator_target, - &ev.join("recursive_aggregation/dkg_aggregator"), - "dkg_aggregator", - ".vk", - ".vk_hash", - ) - .await; - // Decryption aggregator — EVM-targeted (C6 fold + C7 verified inside) - copy_circuit( - &decryption_aggregator_target, - &ev.join("recursive_aggregation/decryption_aggregator"), - "decryption_aggregator", - ".vk", - ".vk_hash", - ) - .await; - // C7 (decrypted_shares_aggregation) — EVM-targeted - copy_circuit( - &threshold_target, - &ev.join("threshold/decrypted_shares_aggregation"), - "decrypted_shares_aggregation", - ".vk", - ".vk_hash", - ) - .await; + // DKG aggregator — EVM-targeted (folds + C5 verified inside) + copy_circuit( + &dkg_aggregator_target, + &ev.join("recursive_aggregation/dkg_aggregator"), + "dkg_aggregator", + ".vk", + ".vk_hash", + ) + .await?; + // Decryption aggregator — EVM-targeted (C6 fold + C7 verified inside) + copy_circuit( + &decryption_aggregator_target, + &ev.join("recursive_aggregation/decryption_aggregator"), + "decryption_aggregator", + ".vk", + ".vk_hash", + ) + .await?; + // C7 (decrypted_shares_aggregation) — EVM-targeted + copy_circuit( + &threshold_target, + &ev.join("threshold/decrypted_shares_aggregation"), + "decrypted_shares_aggregation", + ".vk", + ".vk_hash", + ) + .await?; + } let backend = ZkBackend::new(BBPath::Default(bb_binary), circuits_dir, work_dir); @@ -638,6 +778,27 @@ fn determine_committee( Ok((committee, committee_scores, buffer_nodes)) } +/// Lowest-address committee member after `CommitteeFinalized::sort_by_score` (party 0 / active aggregator). +fn active_aggregator_address( + committee: &[String], + scores: &[String], + e3_id: &E3id, + chain_id: u64, +) -> String { + let mut finalized = CommitteeFinalized { + e3_id: e3_id.clone(), + committee: committee.to_vec(), + scores: scores.to_vec(), + chain_id, + }; + finalized.sort_by_score(); + finalized + .committee + .first() + .cloned() + .expect("committee must be non-empty") +} + fn find_node_index_by_address(nodes: &CiphernodeSystem, address: &str) -> Result { for (index, node) in nodes.iter().enumerate() { if node.address().eq_ignore_ascii_case(address) { @@ -683,6 +844,23 @@ fn count_projected_events(projected: &[&str], event_type: &str) -> usize { projected.iter().filter(|seen| **seen == event_type).count() } +/// Wall seconds between first `start_when` and last `end_when` event in `history` (HLC physical time). +fn history_wall_seconds_between( + history: &[EnclaveEvent], + start_when: F1, + end_when: F2, +) -> Option +where + F1: Fn(&EnclaveEventData) -> bool, + F2: Fn(&EnclaveEventData) -> bool, +{ + let start = history.iter().find(|e| start_when(e.get_data()))?; + let end = history.iter().rfind(|e| end_when(e.get_data()))?; + let start_us = HlcTimestamp::wall_time(start.ts()); + let end_us = HlcTimestamp::wall_time(end.ts()); + (end_us >= start_us).then(|| (end_us - start_us) as f64 / 1_000_000.0) +} + fn publickey_aggregator_marker(data: &EnclaveEventData, e3_id: &E3id) -> Option<&'static str> { match data { EnclaveEventData::CommitteeFinalized(data) if data.e3_id == *e3_id => { @@ -846,9 +1024,22 @@ async fn setup_score_sortition_environment( Ok(()) } +#[derive(Clone, Copy)] +enum PhaseMetric { + WallClock, +} + +impl PhaseMetric { + fn as_str(self) -> &'static str { + match self { + PhaseMetric::WallClock => "wall_clock", + } + } +} + #[derive(Default)] struct Report { - inner: Vec<(String, Duration)>, + inner: Vec<(String, Duration, PhaseMetric)>, } fn repeat(ch: char, num: usize) -> String { @@ -872,10 +1063,14 @@ fn json_escape(s: &str) -> String { } impl Report { - pub fn push(&mut self, repo: (&str, Duration)) { - let (label, dur) = repo; + pub fn push_wall(&mut self, label: &str, dur: Duration) { self.show(label); - self.inner.push((label.to_owned(), dur)); + self.inner + .push((label.to_owned(), dur, PhaseMetric::WallClock)); + } + + pub fn push(&mut self, repo: (&str, Duration)) { + self.push_wall(repo.0, repo.1); } pub fn show(&self, label: &str) { @@ -890,11 +1085,16 @@ impl Report { } pub fn serialize(&self) -> String { - let max_key_len = self.inner.iter().map(|(k, _)| k.len()).max().unwrap_or(0); + let max_key_len = self + .inner + .iter() + .map(|(k, _, _)| k.len()) + .max() + .unwrap_or(0); self.inner .iter() - .map(|(key, duration)| { + .map(|(key, duration, _)| { format!( "{:width$}: {:.3}s", key, @@ -932,8 +1132,7 @@ async fn test_trbfv_actor() -> Result<()> { let setup = Instant::now(); - // Create rng - let rng = create_shared_rng_from_u64(42); + const BENCHMARK_NODE_RNG_BASE: u64 = 42; // Create "trigger" bus let system = EventSystem::new().with_fresh_bus(); @@ -983,11 +1182,21 @@ async fn test_trbfv_actor() -> Result<()> { let cipher = Arc::new(Cipher::from_password("I am the music man.").await?); // Actor system setup - // Seems like you cannot send more than one job at a time to rayon - let concurrent_jobs = 1; + let concurrent_jobs = benchmark_multithread_concurrent_jobs(); + let _fold_verifier_env_guard = (benchmark_proof_aggregation_enabled() + && std::env::var("BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER").is_err()) + .then(|| { + // In-process benchmark has no RPC; same default as pre-registry-fetch harness. + ScopedEnvVar::set( + "BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER", + "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + ) + }); + let slashing_manager_addr = benchmark_slashing_manager_address(); let max_threadroom = Multithread::get_max_threads_minus(1); - let task_pool = Multithread::create_taskpool(max_threadroom, concurrent_jobs); - let multithread_report = MultithreadReport::new(max_threadroom, concurrent_jobs).start(); + let pool_threads = concurrent_jobs.min(max_threadroom).max(1); + let task_pool = Multithread::create_taskpool(pool_threads, concurrent_jobs); + let multithread_report = MultithreadReport::new(pool_threads, concurrent_jobs).start(); // Setup ZK backend for proof generation/verification let (zk_backend, _zk_temp) = setup_test_zk_backend(benchmark_params.preset_subdir).await?; @@ -997,44 +1206,50 @@ async fn test_trbfv_actor() -> Result<()> { // Node 0 stays an observer only because it is excluded from sortition registration. // Adding 20 total nodes: 3 for committee + 3 buffer = 6 selected, 14 unselected .add_group(1, || async { - let addr = rand_eth_addr(&rng); + let node_rng = next_benchmark_node_rng(BENCHMARK_NODE_RNG_BASE); + let addr = rand_eth_addr(&node_rng); println!("Building collector {}!", addr); - CiphernodeBuilder::new(rng.clone(), cipher.clone()) - .testmode_with_history() - .with_shared_taskpool(&task_pool) - .with_multithread_concurrent_jobs(concurrent_jobs) - .with_shared_multithread_report(&multithread_report) - .with_trbfv() - .with_zkproof(zk_backend.clone()) - .testmode_with_signer(PrivateKeySigner::random()) - .with_pubkey_aggregation() - .with_sortition_score() - .with_threshold_plaintext_aggregation() - .testmode_with_forked_bus(bus.event_bus()) - .testmode_ignore_address_check() - .with_logging() - .build() - .await + { + let mut b = CiphernodeBuilder::new(node_rng, cipher.clone()) + .testmode_with_history() + .with_shared_taskpool(&task_pool) + .with_multithread_concurrent_jobs(concurrent_jobs) + .with_shared_multithread_report(&multithread_report) + .with_trbfv() + .with_zkproof(zk_backend.clone()) + .testmode_with_signer(PrivateKeySigner::random()) + .with_pubkey_aggregation() + .with_sortition_score() + .with_threshold_plaintext_aggregation() + .testmode_with_forked_bus(bus.event_bus()) + .testmode_ignore_address_check() + .testmode_with_slashing_manager(slashing_manager_addr) + .with_logging(); + b.build().await + } }) .add_group(19, || async { - let addr = rand_eth_addr(&rng); + let node_rng = next_benchmark_node_rng(BENCHMARK_NODE_RNG_BASE); + let addr = rand_eth_addr(&node_rng); println!("Building normal {}", &addr); - CiphernodeBuilder::new(rng.clone(), cipher.clone()) - .testmode_with_history() - .with_shared_taskpool(&task_pool) - .with_multithread_concurrent_jobs(concurrent_jobs) - .with_shared_multithread_report(&multithread_report) - .with_trbfv() - .with_zkproof(zk_backend.clone()) - .testmode_with_signer(PrivateKeySigner::random()) - .with_pubkey_aggregation() - .with_sortition_score() - .with_threshold_plaintext_aggregation() - .testmode_with_forked_bus(bus.event_bus()) - .testmode_ignore_address_check() - .with_logging() - .build() - .await + { + let mut b = CiphernodeBuilder::new(node_rng, cipher.clone()) + .testmode_with_history() + .with_shared_taskpool(&task_pool) + .with_multithread_concurrent_jobs(concurrent_jobs) + .with_shared_multithread_report(&multithread_report) + .with_trbfv() + .with_zkproof(zk_backend.clone()) + .testmode_with_signer(PrivateKeySigner::random()) + .with_pubkey_aggregation() + .with_sortition_score() + .with_threshold_plaintext_aggregation() + .testmode_with_forked_bus(bus.event_bus()) + .testmode_ignore_address_check() + .testmode_with_slashing_manager(slashing_manager_addr) + .with_logging(); + b.build().await + } }) .simulate_libp2p() .build() @@ -1084,7 +1299,11 @@ async fn test_trbfv_actor() -> Result<()> { // Trigger actor DKG let e3_id = E3id::new("0", 1); - let proof_aggregation_enabled = true; + let proof_aggregation_enabled = benchmark_proof_aggregation_enabled(); + println!( + "Benchmark trbfv: proof_aggregation={proof_aggregation_enabled}, preset={}, pool_threads={pool_threads}, max_concurrent_jobs={concurrent_jobs}", + benchmark_params.preset_subdir + ); let e3_requested = E3Requested { e3_id: e3_id.clone(), @@ -1117,10 +1336,8 @@ async fn test_trbfv_actor() -> Result<()> { buffer_nodes.len() )); - let active_aggregator_addr = committee - .first() - .cloned() - .expect("committee should have an active aggregator"); + let active_aggregator_addr = + active_aggregator_address(&committee, &committee_scores, &e3_id, chain_id); let active_aggregator_index = find_node_index_by_address(&nodes, &active_aggregator_addr)?; println!( @@ -1256,10 +1473,26 @@ async fn test_trbfv_actor() -> Result<()> { "Active aggregator: last event must be PublicKeyAggregated" ); - report.push(( + if let Some(secs) = history_wall_seconds_between( + &active_aggregator_history, + |d| { + matches!( + d, + EnclaveEventData::PkAggregationProofPending(data) if data.e3_id == e3_id + ) + }, + |d| matches!(d, EnclaveEventData::PublicKeyAggregated(data) if data.e3_id == e3_id), + ) { + report.push_wall( + "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", + Duration::from_secs_f64(secs), + ); + } + + report.push_wall( "ThresholdShares -> PublicKeyAggregated", shares_to_pubkey_agg_timer.elapsed(), - )); + ); report.push(( "E3Request -> PublicKeyAggregated", @@ -1433,10 +1666,26 @@ async fn test_trbfv_actor() -> Result<()> { "PlaintextAggregated must be the last active aggregator completion event" ); - report.push(( + if let Some(secs) = history_wall_seconds_between( + &active_aggregator_history[active_aggregator_pubkey_history_len..], + |d| { + matches!( + d, + EnclaveEventData::AggregationProofPending(data) if data.e3_id == e3_id + ) + }, + |d| matches!(d, EnclaveEventData::PlaintextAggregated(data) if data.e3_id == e3_id), + ) { + report.push_wall( + "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", + Duration::from_secs_f64(secs), + ); + } + + report.push_wall( "Ciphertext published -> PlaintextAggregated", publishing_ct_timer.elapsed(), - )); + ); let (plaintext, decryption_aggregator_proofs) = h .iter() @@ -1463,10 +1712,12 @@ async fn test_trbfv_actor() -> Result<()> { ) })?; - assert!( - !decryption_aggregator_proofs.is_empty(), - "DecryptionAggregator proofs should be present in PlaintextAggregated" - ); + if proof_aggregation_enabled { + assert!( + !decryption_aggregator_proofs.is_empty(), + "DecryptionAggregator proofs should be present in PlaintextAggregated when proof_aggregation_enabled" + ); + } if let Ok(path) = std::env::var("BENCHMARK_FOLDED_OUTPUT") { if let (Some(dkg_proof), Some(dec_proof)) = ( @@ -1546,14 +1797,15 @@ async fn test_trbfv_actor() -> Result<()> { .collect::>() .join(",\n"); - let timings_json = report + let phase_timings_json = report .inner .iter() - .map(|(label, duration)| { + .map(|(label, duration, metric)| { format!( - " {{\"label\": \"{}\", \"seconds\": {:.9}}}", + " {{\"label\": \"{}\", \"seconds\": {:.9}, \"metric\": \"{}\"}}", json_escape(label), - duration.as_secs_f64() + duration.as_secs_f64(), + metric.as_str() ) }) .collect::>() @@ -1585,10 +1837,48 @@ async fn test_trbfv_actor() -> Result<()> { String::from(" \"folded_artifacts\": null\n") }; + let dkg_fold_verifier_json = benchmark_dkg_fold_attestation_verifier_address() + .map(|addr| format!(" \"dkg_fold_attestation_verifier\": \"{addr}\",\n")) + .unwrap_or_default(); + + let benchmark_mode = + std::env::var("BENCHMARK_MODE").unwrap_or_else(|_| "insecure".to_string()); + let benchmark_config_json = format!( + concat!( + " \"benchmark_config\": {{\n", + " \"mode\": \"{}\",\n", + " \"bfv_preset_subdir\": \"{}\",\n", + " \"bfv_preset\": \"{:?}\",\n", + " \"lambda\": {},\n", + " \"proof_aggregation_enabled\": {},\n", + " \"multithread_concurrent_jobs\": {},\n", + " \"committee_h\": {},\n", + " \"committee_n\": {},\n", + " \"committee_t\": {},\n", + " \"nodes_spawned\": {},\n", + " \"network_model\": \"in_process_bus\",\n", + " \"testmode_harness\": true\n", + " }},\n" + ), + json_escape(&benchmark_mode), + json_escape(benchmark_params.preset_subdir), + benchmark_params.bfv_preset, + benchmark_params.lambda, + proof_aggregation_enabled, + concurrent_jobs, + threshold_n, // micro committee: H == N_PARTIES + threshold_n, + threshold_m, + 20usize, + ); + let summary_json = format!( concat!( "{{\n", " \"integration_test\": \"test_trbfv_actor\",\n", + "{benchmark_config_json}", + " \"proof_aggregation_enabled\": {},\n", + "{dkg_fold_verifier_json}", " \"multithread\": {{\n", " \"rayon_threads\": {},\n", " \"max_simultaneous_rayon_tasks\": {},\n", @@ -1598,19 +1888,23 @@ async fn test_trbfv_actor() -> Result<()> { "{}\n", " ],\n", " \"operation_timings_total_seconds\": {:.9},\n", - " \"timings_seconds\": [\n", + " \"operation_timings_metric\": \"tracked_job_wall\",\n", + " \"phase_timings\": [\n", "{}\n", " ],\n", "{}", "}}\n" ), + proof_aggregation_enabled, mt_report.rayon_threads(), mt_report.max_simultaneous_rayon_tasks(), mt_report.cores_available(), operation_timings_json, mt_report.tracked_total_seconds(), - timings_json, - folded_section + phase_timings_json, + folded_section, + benchmark_config_json = benchmark_config_json, + dkg_fold_verifier_json = dkg_fold_verifier_json, ); fs::write(&path, summary_json)?; println!("Wrote benchmark summary to {path}"); @@ -2132,8 +2426,8 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { .send(TakeEvents::::new(28)) .await?; - let actual_pk_commitment_1 = match history.events.last().cloned().unwrap().into_data() { - e3_events::EnclaveEventData::PublicKeyAggregated(ev) => ev.pk_commitment, + let actual_pubkey_agg_1 = match history.events.last().cloned().unwrap().into_data() { + e3_events::EnclaveEventData::PublicKeyAggregated(ev) => ev, other => panic!("expected PublicKeyAggregated, got {other:?}"), }; assert_eq!( @@ -2142,8 +2436,10 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { pubkey: ArcBytes::from_bytes(&test_pubkey.to_bytes()), e3_id: E3id::new("1234", 1), nodes: OrderedSet::from(eth_addrs.clone()), - pk_commitment: actual_pk_commitment_1, + committee_addresses: actual_pubkey_agg_1.committee_addresses, + pk_commitment: actual_pubkey_agg_1.pk_commitment, dkg_aggregator_proof: None, + dkg_attestation_bundle: None, } .into() ); @@ -2173,8 +2469,8 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { .send(TakeEvents::::new(8)) .await?; - let actual_pk_commitment_2 = match history.events.last().cloned().unwrap().into_data() { - e3_events::EnclaveEventData::PublicKeyAggregated(ev) => ev.pk_commitment, + let actual_pubkey_agg_2 = match history.events.last().cloned().unwrap().into_data() { + e3_events::EnclaveEventData::PublicKeyAggregated(ev) => ev, other => panic!("expected PublicKeyAggregated, got {other:?}"), }; assert_eq!( @@ -2183,8 +2479,10 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { pubkey: ArcBytes::from_bytes(&test_pubkey.to_bytes()), e3_id: E3id::new("1234", 2), nodes: OrderedSet::from(eth_addrs.clone()), - pk_commitment: actual_pk_commitment_2, + committee_addresses: actual_pubkey_agg_2.committee_addresses, + pk_commitment: actual_pubkey_agg_2.pk_commitment, dkg_aggregator_proof: None, + dkg_attestation_bundle: None, } .into() ); diff --git a/crates/trbfv/src/gen_esi_sss.rs b/crates/trbfv/src/gen_esi_sss.rs index 213d15e816..e9950d449b 100644 --- a/crates/trbfv/src/gen_esi_sss.rs +++ b/crates/trbfv/src/gen_esi_sss.rs @@ -11,8 +11,9 @@ use crate::{ }; use anyhow::{Context, Result}; use e3_crypto::{Cipher, SensitiveBytes}; -use e3_utils::{utility_types::ArcBytes, SharedRng}; +use e3_utils::utility_types::ArcBytes; use fhe::trbfv::ShareManager; +use rand::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use tracing::info; @@ -68,8 +69,8 @@ struct InnerResponse { /// When implementing multiple ciphertext outputs decryptions, we are going to need multiple smudging noise polynomials, /// so we are generating a vector of smudging noise secret shares (esi_sss) instead of just one in anticipation of that change. /// We will also need to ensure that all of them are committed to the pk_generation circuit. -pub fn gen_esi_sss( - rng: &SharedRng, +pub fn gen_esi_sss( + rng: &mut R, cipher: &Cipher, req: GenEsiSssRequest, ) -> Result { @@ -89,7 +90,7 @@ pub fn gen_esi_sss( let esi_sss = vec![SharedSecret::from( share_manager - .generate_secret_shares_from_poly(e_sm_poly.into(), &mut *rng.lock().unwrap()) + .generate_secret_shares_from_poly(e_sm_poly.into(), rng) .context("Failed to generate secret shares from poly")?, )]; diff --git a/crates/trbfv/src/gen_pk_share_and_sk_sss.rs b/crates/trbfv/src/gen_pk_share_and_sk_sss.rs index 06144baab9..01377b8a7d 100644 --- a/crates/trbfv/src/gen_pk_share_and_sk_sss.rs +++ b/crates/trbfv/src/gen_pk_share_and_sk_sss.rs @@ -12,13 +12,14 @@ use crate::{ }; use anyhow::Result; use e3_crypto::{Cipher, SensitiveBytes}; -use e3_utils::{utility_types::ArcBytes, SharedRng}; +use e3_utils::utility_types::ArcBytes; use fhe::{ bfv::SecretKey, mbfv::{CommonRandomPoly, PublicKeyShare}, trbfv::{ShareManager, TRBFV}, }; use fhe_traits::Serialize as FheSerialize; +use rand::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use std::ops::Deref; use tracing::info; @@ -105,8 +106,8 @@ struct InnerResponse { pub e_sm_raw: ArcBytes, } -pub fn gen_pk_share_and_sk_sss( - rng: &SharedRng, +pub fn gen_pk_share_and_sk_sss( + rng: &mut R, cipher: &Cipher, req: GenPkShareAndSkSssRequest, ) -> Result { @@ -122,9 +123,8 @@ pub fn gen_pk_share_and_sk_sss( "gen_pk_share_and_sk_sss: n={}, t={}", num_ciphernodes, threshold ); - let sk_share = { SecretKey::random(¶ms, &mut *rng.lock().unwrap()) }; - let (pk0_share, _, _, eek) = - { PublicKeyShare::new_extended(&sk_share, crp.clone(), &mut *rng.lock().unwrap())? }; + let sk_share = SecretKey::random(¶ms, rng); + let (pk0_share, _, _, eek) = PublicKeyShare::new_extended(&sk_share, crp.clone(), rng)?; let pk_share = PublicKeyShare::deserialize(&pk0_share.to_bytes(), ¶ms, crp.clone())?; @@ -132,11 +132,7 @@ pub fn gen_pk_share_and_sk_sss( let trbfv = TRBFV::new(num_ciphernodes as usize, threshold as usize, params.clone())?; let share_manager_for_esm = ShareManager::new(num_ciphernodes as usize, threshold as usize, params.clone()); - let esi_coeffs = trbfv.generate_smudging_error( - req.num_ciphertexts, - req.lambda, - &mut *rng.lock().unwrap(), - )?; + let esi_coeffs = trbfv.generate_smudging_error(req.num_ciphertexts, req.lambda, rng)?; let e_sm_rns = share_manager_for_esm.bigints_to_poly(&esi_coeffs)?; let e_sm_raw = ArcBytes::from_bytes(&e_sm_rns.deref().to_bytes()); @@ -150,9 +146,8 @@ pub fn gen_pk_share_and_sk_sss( let sk_raw = ArcBytes::from_bytes(&sk_poly.to_bytes()); info!("gen_pk_share_and_sk_sss:generate_secret_shares_from_poly..."); - let sk_sss = SharedSecret::from({ - share_manager.generate_secret_shares_from_poly(sk_poly, &mut *rng.lock().unwrap())? - }); + let sk_sss = + SharedSecret::from({ share_manager.generate_secret_shares_from_poly(sk_poly, rng)? }); ( InnerResponse { diff --git a/crates/zk-prover/Cargo.toml b/crates/zk-prover/Cargo.toml index 0f8e9b9130..d1c972f2a6 100644 --- a/crates/zk-prover/Cargo.toml +++ b/crates/zk-prover/Cargo.toml @@ -48,6 +48,7 @@ walkdir = "2.5" [dev-dependencies] e3-test-helpers = { workspace = true } +e3-evm = { workspace = true } ark-bn254 = { workspace = true } ark-ff = { workspace = true } fhe-traits = { workspace = true } diff --git a/crates/zk-prover/scripts/build_fixtures.sh b/crates/zk-prover/scripts/build_fixtures.sh index c7d17a3d68..651f424e86 100644 --- a/crates/zk-prover/scripts/build_fixtures.sh +++ b/crates/zk-prover/scripts/build_fixtures.sh @@ -22,3 +22,31 @@ fi echo "Building circuits..." pnpm install && pnpm build:circuits + +# Keep integration-test fixture in sync when the dummy circuit is built. +dummy_artifact="./circuits/bin/dummy/dummy.json" +fixture="./crates/zk-prover/tests/fixtures/dummy.json" +normalize_compiled_circuit_paths() { + # Noir emits machine-local absolute paths in file_map; keep fixtures stable. + jq ' + if .file_map then + .file_map |= with_entries( + .value |= if (.path | type) == "string" then + .path |= ( + if test("^/") and test("circuits/") then + sub("^.*?circuits/"; "circuits/") + else . + end + ) + else . + end + ) + else . + end + ' +} + +if [ -f "$dummy_artifact" ]; then + mkdir -p "$(dirname "$fixture")" + normalize_compiled_circuit_paths <"$dummy_artifact" >"$fixture" +fi diff --git a/crates/zk-prover/src/actors/accusation_manager.rs b/crates/zk-prover/src/actors/accusation_manager.rs index 162a21e703..e6dceb21b3 100644 --- a/crates/zk-prover/src/actors/accusation_manager.rs +++ b/crates/zk-prover/src/actors/accusation_manager.rs @@ -27,7 +27,8 @@ //! | C7 | On-chain verification | Not handled here (on-chain verifier) | use std::collections::{HashMap, HashSet}; -use std::time::Duration; +use std::sync::Arc; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; use actix::{Actor, Addr, AsyncContext, Context, Handler, SpawnHandle}; use alloy::primitives::{keccak256, Address, Bytes, U256}; @@ -40,19 +41,51 @@ use e3_events::{ ComputeResponseKind, CorrelationId, E3id, EnclaveEvent, EnclaveEventData, EventContext, EventPublisher, EventSubscriber, EventType, PartyProofsToVerify, ProofFailureAccusation, ProofType, ProofVerificationFailed, ProofVerificationPassed, Sequenced, SignedProofPayload, - SlashExecuted, TypedEvent, VerifyShareProofsRequest, ZkRequest, ZkResponse, + SlashExecuted, TypedEvent, VerifyShareProofsRequest, ZkRequest, ZkResponse, VOTE_DOMAIN_NAME, + VOTE_DOMAIN_VERSION, VOTE_TYPEHASH_STR, }; use e3_utils::{ArcBytes, NotifySync}; use tracing::{error, info, warn}; /// How long to wait for votes before declaring the accusation inconclusive. const DEFAULT_VOTE_TIMEOUT: Duration = Duration::from_secs(300); // 5 minutes +/// Default clock-skew allowance when validating peer-stamped accusation deadlines. +#[cfg(test)] +const DEFAULT_ACCUSATION_DEADLINE_SKEW_SECS: u64 = 30; + +/// Abstraction over wall-clock time so the deadline-stamping logic is +/// deterministically testable. Production uses [`SystemClock`], which reads +/// `SystemTime::now()`; tests can inject a mock clock that returns fixed +/// timestamps. +pub trait Clock: Send + Sync + 'static { + /// Current Unix time in seconds. Returns `0` if the platform clock is + /// pre-`UNIX_EPOCH` (a broken clock should not silently produce + /// signatures that look valid forever — the on-chain check will then + /// reject the resulting deadline immediately). + fn unix_now_secs(&self) -> u64; +} + +/// Production clock backed by `SystemTime::now()`. +pub struct SystemClock; -/// An active accusation awaiting votes from committee members. +impl Clock for SystemClock { + fn unix_now_secs(&self) -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_secs()) + .unwrap_or(0) + } +} + +/// An active accusation awaiting agreement votes from committee members. +/// +/// There is no `votes_against` field: a peer who finds the disputed proof +/// passes simply stays silent rather than broadcasting a signed disagreement +/// (see `AccusationVote` docstring for rationale). The accusation runs to +/// quorum or to `vote_timeout`. struct PendingAccusation { accusation: ProofFailureAccusation, votes_for: Vec, - votes_against: Vec, /// Handle to the timeout future so it can be cancelled on early quorum. timeout_handle: Option, /// The EventContext from when this accusation was created — used for timeout emission. @@ -65,6 +98,13 @@ struct ReceivedProofData { data_hash: [u8; 32], /// `true` if our local verification passed, `false` if it failed. verification_passed: bool, + /// Raw `abi.encode(proof.data, proof.public_signals)` — preimage of + /// `data_hash`. Forwarded to the on-chain slashing contract so it can + /// recompute and verify the dataHash bound in voter signatures. Empty + /// only on paths where the raw bytes weren't available locally; those + /// paths can still slash, but they fall back to off-chain trust for + /// the evidence binding. + evidence: Bytes, } /// Tracks an in-flight ZK re-verification for a forwarded C3a/C3b proof. @@ -73,6 +113,9 @@ struct PendingReVerification { data_hash: [u8; 32], accused: Address, proof_type: ProofType, + /// Evidence preimage bytes from the forwarded proof, used to populate + /// `ReceivedProofData.evidence` after ZK re-verification completes. + evidence: Bytes, } /// Manages the off-chain accusation quorum protocol. @@ -107,6 +150,9 @@ pub struct AccusationManager { my_address: Address, signer: PrivateKeySigner, + /// On-chain `SlashingManager` address (EIP-712 `verifyingContract` for vote signatures). + slashing_manager: Address, + /// All committee member addresses for this E3. committee: Vec
, /// Quorum threshold — matches the cryptographic threshold M. @@ -133,18 +179,65 @@ pub struct AccusationManager { /// Vote timeout duration. vote_timeout: Duration, + /// Registry-wide off-chain freshness window (seconds) applied when stamping + /// `AccusationVote.deadline`. Fetched once per process from + /// `CiphernodeRegistry.accusationVoteValidity()` so a governance change + /// requires a node restart to take effect — same lifecycle as the fold + /// attestation verifier. + vote_validity_secs: u64, + /// Clock-skew allowance when validating peer accusation deadlines. + accusation_deadline_skew_secs: u64, + + /// Wall-clock source used to derive accusation deadlines. Production uses + /// [`SystemClock`]; tests can inject a deterministic mock. + clock: Arc, + /// BFV preset for circuit artifact resolution. params_preset: e3_fhe_params::BfvPreset, } impl AccusationManager { + /// Construct an actor with the production [`SystemClock`]. Use + /// [`AccusationManager::new_with_clock`] in tests that need deterministic + /// timestamps. pub fn new( bus: &BusHandle, e3_id: E3id, signer: PrivateKeySigner, + slashing_manager: Address, committee: Vec
, threshold_m: usize, + vote_validity_secs: u64, + accusation_deadline_skew_secs: u64, params_preset: e3_fhe_params::BfvPreset, + ) -> Self { + Self::new_with_clock( + bus, + e3_id, + signer, + slashing_manager, + committee, + threshold_m, + vote_validity_secs, + accusation_deadline_skew_secs, + params_preset, + Arc::new(SystemClock), + ) + } + + /// Construct an actor with an explicit [`Clock`]. Allows unit tests to + /// drive deadline computation without touching wall-clock time. + pub fn new_with_clock( + bus: &BusHandle, + e3_id: E3id, + signer: PrivateKeySigner, + slashing_manager: Address, + committee: Vec
, + threshold_m: usize, + vote_validity_secs: u64, + accusation_deadline_skew_secs: u64, + params_preset: e3_fhe_params::BfvPreset, + clock: Arc, ) -> Self { let my_address = signer.address(); Self { @@ -152,6 +245,7 @@ impl AccusationManager { e3_id, my_address, signer, + slashing_manager, committee, threshold_m, pending: HashMap::new(), @@ -160,6 +254,9 @@ impl AccusationManager { buffered_votes: HashMap::new(), pending_reverifications: HashMap::new(), vote_timeout: DEFAULT_VOTE_TIMEOUT, + vote_validity_secs, + accusation_deadline_skew_secs, + clock, params_preset, } } @@ -168,11 +265,25 @@ impl AccusationManager { bus: &BusHandle, e3_id: E3id, signer: PrivateKeySigner, + slashing_manager: Address, committee: Vec
, threshold_m: usize, + vote_validity_secs: u64, + accusation_deadline_skew_secs: u64, params_preset: e3_fhe_params::BfvPreset, ) -> Addr { - let addr = Self::new(bus, e3_id, signer, committee, threshold_m, params_preset).start(); + let addr = Self::new( + bus, + e3_id, + signer, + slashing_manager, + committee, + threshold_m, + vote_validity_secs, + accusation_deadline_skew_secs, + params_preset, + ) + .start(); bus.subscribe(EventType::ProofVerificationFailed, addr.clone().into()); bus.subscribe(EventType::ProofVerificationPassed, addr.clone().into()); bus.subscribe(EventType::ProofFailureAccusation, addr.clone().into()); @@ -187,6 +298,48 @@ impl AccusationManager { addr } + // ─── Deadline computation ──────────────────────────────────────────── + + /// Compute the on-chain vote-validity deadline (Unix seconds) the accuser + /// stamps on a fresh accusation. Voters then sign this exact value so the + /// aggregated evidence carries one shared deadline that `SlashingManager` + /// checks via `block.timestamp <= deadline`. + /// + /// `vote_validity_secs` is the registry-wide window fetched from + /// `CiphernodeRegistry.accusationVoteValidity()` at process startup — + /// governance can shorten or extend it; live nodes only pick up the new + /// value on restart. + /// + /// `saturating_add` guards against `u64` overflow in the unlikely event + /// governance sets the validity to a near-`u64::MAX` value. + fn compute_deadline(&self) -> u64 { + self.clock + .unix_now_secs() + .saturating_add(self.vote_validity_secs) + } + + /// Validate a peer-provided accusation deadline against this node's local + /// vote-validity policy and wall clock. + /// + /// Accept iff: + /// - validity is enabled (`vote_validity_secs > 0`) + /// - deadline is strictly in the future + /// - deadline is not farther than `now + vote_validity_secs + skew` + fn is_peer_deadline_acceptable( + deadline: u64, + now: u64, + vote_validity_secs: u64, + skew_secs: u64, + ) -> bool { + if vote_validity_secs == 0 { + return false; + } + let max_deadline = now + .saturating_add(vote_validity_secs) + .saturating_add(skew_secs); + deadline > now && deadline <= max_deadline + } + // ─── Accusation ID computation ─────────────────────────────────────── /// Compute a deterministic ID for an accusation based on its key fields. @@ -212,22 +365,25 @@ impl AccusationManager { // ─── Signing / Verification ────────────────────────────────────────── - fn sign_accusation_digest(&self, accusation: &ProofFailureAccusation) -> Vec { + fn sign_accusation_digest( + &self, + accusation: &ProofFailureAccusation, + ) -> Result, alloy::signers::Error> { let digest = Self::accusation_digest(accusation); - self.signer - .sign_message_sync(&digest) - .map(|sig| sig.as_bytes().to_vec()) - .unwrap_or_default() + let sig = self.signer.sign_message_sync(&digest)?; + Ok(sig.as_bytes().to_vec()) } /// Structured digest for ECDSA signing of accusations. /// - /// Uses a typehash + `abi.encode` pattern matching `ProofPayload::digest()`: + /// Off-chain only — this digest never reaches the chain. Includes `deadline` + /// so peers can verify the accuser's chosen on-chain validity window has not + /// been tampered with in transit: /// ```text /// keccak256(abi.encode( /// ACCUSATION_TYPEHASH, /// chainId, e3Id, accuser, accused, proofType, - /// dataHash + /// dataHash, deadline /// )) /// ``` fn accusation_digest(accusation: &ProofFailureAccusation) -> [u8; 32] { @@ -237,7 +393,7 @@ impl AccusationManager { .try_into() .expect("E3id should be valid U256"); let typehash: [u8; 32] = keccak256( - "ProofFailureAccusation(uint256 chainId,uint256 e3Id,address accuser,address accused,uint256 proofType,bytes32 dataHash)" + "ProofFailureAccusation(uint256 chainId,uint256 e3Id,address accuser,address accused,uint256 proofType,bytes32 dataHash,uint256 deadline)" ).into(); let encoded = ( typehash, @@ -247,6 +403,7 @@ impl AccusationManager { accusation.accused, U256::from(accusation.proof_type as u8), accusation.data_hash, + U256::from(accusation.deadline), ) .abi_encode(); keccak256(&encoded).into() @@ -266,53 +423,94 @@ impl AccusationManager { } } - fn sign_vote_digest(&self, vote: &AccusationVote) -> Vec { - let digest = Self::vote_digest(vote); - self.signer - .sign_message_sync(&digest) - .map(|sig| sig.as_bytes().to_vec()) - .unwrap_or_default() + #[cfg_attr(test, allow(dead_code))] + fn sign_vote_digest(&self, vote: &AccusationVote) -> Result, alloy::signers::Error> { + let digest = Self::vote_digest(vote, self.slashing_manager); + // `sign_hash_sync` signs the raw 32-byte hash without EIP-191 wrapping, + // which is what EIP-712 requires (`digest` is already the + // `\x19\x01 || domainSeparator || structHash` hash). + let sig = self.signer.sign_hash_sync(&digest.into())?; + Ok(sig.as_bytes().to_vec()) } - /// Structured digest for ECDSA signing of votes. + /// Canonical EIP-712 domain separator for vote signatures. /// - /// ```text - /// keccak256(abi.encode( - /// VOTE_TYPEHASH, - /// chainId, e3Id, accusationId, voter, agrees, - /// dataHash - /// )) - /// ``` - fn vote_digest(vote: &AccusationVote) -> [u8; 32] { + /// Must match `SlashingManager`'s domain construction exactly. The `name` + /// literal is `EIP712_DOMAIN_NAME` in the Solidity contract (see + /// `packages/enclave-contracts/contracts/slashing/SlashingManager.sol`); + /// keep these two strings in lockstep — divergence silently breaks + /// `ECDSA.recover` on chain. + fn vote_domain_separator(chain_id: u64, verifying_contract: Address) -> [u8; 32] { + let domain_typehash: [u8; 32] = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)", + ) + .into(); + let name_hash: [u8; 32] = keccak256(VOTE_DOMAIN_NAME).into(); + let version_hash: [u8; 32] = keccak256(VOTE_DOMAIN_VERSION).into(); + let encoded = ( + domain_typehash, + name_hash, + version_hash, + U256::from(chain_id), + verifying_contract, + ) + .abi_encode(); + keccak256(&encoded).into() + } + + /// Canonical EIP-712 typed-data hash for a vote. + /// + /// `keccak256("\x19\x01" || domainSeparator || structHash)` where the struct + /// matches `SlashingManager.VOTE_TYPEHASH`: + /// `AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bytes32 dataHash,uint256 deadline)`. + /// + /// `AccusationVote` no longer carries an `agrees` field. The gossip wire + /// transmits only agreements; the on-chain verifier treats every submitted + /// signature as an affirmative vote. See the struct's docstring in + /// `e3_events::accusation_vote` for rationale. + /// + /// Exposed `pub` so the Anvil parity test in + /// `crates/zk-prover/tests/slashing_integration_tests.rs` can sign votes + /// through the **same** code path the production actor uses — if the + /// digest drifts from on-chain `_verifyVotes`, the parity test reverts on + /// chain immediately rather than allowing the actor to ship broken + /// signatures. + pub fn vote_digest(vote: &AccusationVote, verifying_contract: Address) -> [u8; 32] { let e3_id_u256: U256 = vote .e3_id .clone() .try_into() .expect("E3id should be valid U256"); - let typehash: [u8; 32] = keccak256( - "AccusationVote(uint256 chainId,uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)" - ).into(); - let encoded = ( - typehash, - U256::from(vote.e3_id.chain_id()), - e3_id_u256, - vote.accusation_id, - vote.voter, - vote.agrees, - vote.data_hash, + let typehash: [u8; 32] = keccak256(VOTE_TYPEHASH_STR).into(); + let struct_hash: [u8; 32] = keccak256( + &( + typehash, + e3_id_u256, + vote.accusation_id, + vote.voter, + vote.data_hash, + U256::from(vote.deadline), + ) + .abi_encode(), ) - .abi_encode(); - keccak256(&encoded).into() + .into(); + let domain = Self::vote_domain_separator(vote.e3_id.chain_id(), verifying_contract); + let mut buf = Vec::with_capacity(2 + 32 + 32); + buf.push(0x19); + buf.push(0x01); + buf.extend_from_slice(&domain); + buf.extend_from_slice(&struct_hash); + keccak256(&buf).into() } fn verify_vote_signature(&self, vote: &AccusationVote) -> bool { - let digest = Self::vote_digest(vote); + let digest = Self::vote_digest(vote, self.slashing_manager); let sig = match alloy::primitives::Signature::try_from(vote.signature.extract_bytes().as_ref()) { Ok(s) => s, Err(_) => return false, }; - match sig.recover_address_from_msg(&digest) { + match sig.recover_address_from_prehash(&digest.into()) { Ok(addr) => addr == vote.voter, Err(_) => false, } @@ -330,6 +528,10 @@ impl AccusationManager { ec: &EventContext, ctx: &mut Context, ) { + if event.e3_id != self.e3_id { + return; + } + let accused_address = if event.accused_address == Address::ZERO { if let Some(&addr) = self.committee.get(event.accused_party_id as usize) { warn!( @@ -348,12 +550,30 @@ impl AccusationManager { event.accused_address }; - // Cache the failed verification result + if !self.committee.contains(&accused_address) { + warn!( + "Ignoring proof failure for {} — not on E3 {} committee", + accused_address, self.e3_id + ); + return; + } + + // Cache the failed verification result. + // Evidence preimage = `abi.encode(proof.data, public_signals)` — matches + // the on-chain `keccak256(evidence) == dataHash` check in SlashingManager. + let evidence = Bytes::from( + ( + Bytes::copy_from_slice(&event.signed_payload.payload.proof.data), + Bytes::copy_from_slice(&event.signed_payload.payload.proof.public_signals), + ) + .abi_encode(), + ); self.received_data.insert( (accused_address, event.proof_type), ReceivedProofData { data_hash: event.data_hash, verification_passed: false, + evidence, }, ); @@ -387,12 +607,29 @@ impl AccusationManager { ec: &EventContext, ctx: &mut Context, ) { - // Cache as a failed verification for voting on future accusations + if data.e3_id != self.e3_id { + return; + } + + if !self.committee.contains(&data.accused_address) { + warn!( + "Ignoring commitment violation for {} — not on E3 {} committee", + data.accused_address, self.e3_id + ); + return; + } + + // Cache as a failed verification for voting on future accusations. + // `data.evidence` carries the raw `abi.encode(proof.data, public_signals)` + // preimage of `data_hash`, populated by the consistency checker. Slashing + // via this path now binds voter signatures to evidence bytes on-chain + // just like the ProofVerificationFailed path. self.received_data.insert( (data.accused_address, data.proof_type), ReceivedProofData { data_hash: data.data_hash, verification_passed: false, + evidence: data.evidence.clone(), }, ); @@ -423,6 +660,14 @@ impl AccusationManager { ec: &EventContext, ctx: &mut Context, ) { + if !self.committee.contains(&accused_address) { + warn!( + "Refusing accusation against {} — not on E3 {} committee", + accused_address, self.e3_id + ); + return; + } + let key = (accused_address, proof_type); // Dedup: don't create multiple accusations for the same (accused, proof_type) @@ -434,6 +679,22 @@ impl AccusationManager { return; } + // Governance-disabled validity window means no accusation voting should + // be produced by this node. + if self.vote_validity_secs == 0 { + warn!( + "Refusing accusation initiation for {:?} on E3 {}: vote_validity_secs is 0", + accused_address, self.e3_id + ); + self.accused_proofs.remove(&key); + return; + } + + // Pick the on-chain validity deadline once per accusation. Every voter + // (including ourselves below) signs the same value; otherwise the + // aggregated evidence cannot be encoded as a single `deadline`. + let deadline = self.compute_deadline(); + // Create the accusation let mut accusation = ProofFailureAccusation { e3_id: self.e3_id.clone(), @@ -442,10 +703,18 @@ impl AccusationManager { accused_party_id, proof_type, data_hash, + deadline, signed_payload: forwarded_payload, signature: ArcBytes::default(), }; - accusation.signature = ArcBytes::from_bytes(&self.sign_accusation_digest(&accusation)); + match self.sign_accusation_digest(&accusation) { + Ok(sig) => accusation.signature = ArcBytes::from_bytes(&sig), + Err(err) => { + error!("Failed to sign ProofFailureAccusation: {err}"); + self.accused_proofs.remove(&key); + return; + } + } let accusation_id = Self::accusation_id(&accusation); @@ -460,16 +729,22 @@ impl AccusationManager { return; } - // Cast own vote (agrees: true) + // Cast our own agreement vote (we just observed the failure locally). let mut own_vote = AccusationVote { e3_id: self.e3_id.clone(), accusation_id, voter: self.my_address, - agrees: true, data_hash, + deadline, signature: ArcBytes::default(), }; - own_vote.signature = ArcBytes::from_bytes(&self.sign_vote_digest(&own_vote)); + match self.sign_vote_digest(&own_vote) { + Ok(sig) => own_vote.signature = ArcBytes::from_bytes(&sig), + Err(err) => { + error!("Failed to sign own AccusationVote: {err}"); + return; + } + } if let Err(err) = self.bus.publish(own_vote.clone(), ec.clone()) { error!("Failed to broadcast own AccusationVote: {err}"); @@ -486,7 +761,6 @@ impl AccusationManager { PendingAccusation { accusation, votes_for: vec![own_vote], - votes_against: Vec::new(), timeout_handle: Some(timeout_handle), ec: ec.clone(), }, @@ -517,6 +791,29 @@ impl AccusationManager { return; } + let now = self.clock.unix_now_secs(); + if !Self::is_peer_deadline_acceptable( + accusation.deadline, + now, + self.vote_validity_secs, + self.accusation_deadline_skew_secs, + ) { + let max_deadline = now + .saturating_add(self.vote_validity_secs) + .saturating_add(self.accusation_deadline_skew_secs); + warn!( + "Ignoring accusation from {} — deadline {} outside local validity window \ + (now={}, vote_validity_secs={}, skew_secs={}, max_accepted_deadline={})", + accusation.accuser, + accusation.deadline, + now, + self.vote_validity_secs, + self.accusation_deadline_skew_secs, + max_deadline + ); + return; + } + // Verify accuser is in committee if !self.committee.contains(&accusation.accuser) { warn!( @@ -556,11 +853,25 @@ impl AccusationManager { return; } - // Determine our vote based on our local verification state + // Determine our position based on our local verification state. + // + // The gossip wire no longer carries disagreement: if our local check + // *passed*, we stay silent (no broadcast, no pending state). The + // accusation will then either reach quorum from other agreeing peers + // or time out as Inconclusive. Only the "we also saw it fail" branch + // and the "we don't have local data yet (C3a/C3b)" branch proceed + // below. let key = (accusation.accused, accusation.proof_type); - let (agrees, our_data_hash) = if let Some(received) = self.received_data.get(&key) { - // We have the data — did our verification also fail? - (!received.verification_passed, received.data_hash) + let our_data_hash = if let Some(received) = self.received_data.get(&key) { + if received.verification_passed { + info!( + "Local verification of {:?} from {} passed — abstaining \ + (no disagreement vote on the wire)", + accusation.proof_type, accusation.accused + ); + return; + } + received.data_hash } else if let Some(ref forwarded) = accusation.signed_payload { // C3a/C3b case: we didn't receive this proof directly. // Validate the forwarded payload's ECDSA, then dispatch async ZK re-verification. @@ -592,6 +903,12 @@ impl AccusationManager { } let data_hash = Self::compute_payload_hash(forwarded); + let evidence: Bytes = ( + Bytes::copy_from_slice(&forwarded.payload.proof.data), + Bytes::copy_from_slice(&forwarded.payload.proof.public_signals), + ) + .abi_encode() + .into(); let accused_party_id = accusation.accused_party_id; let forwarded_clone = forwarded.clone(); @@ -612,7 +929,6 @@ impl AccusationManager { PendingAccusation { accusation, votes_for: Vec::new(), - votes_against: Vec::new(), timeout_handle: Some(timeout_handle), ec: ec.clone(), }, @@ -634,6 +950,7 @@ impl AccusationManager { data_hash, accused: key.0, proof_type: key.1, + evidence, }, ); @@ -666,22 +983,28 @@ impl AccusationManager { return; }; - // Cast vote + // We saw the proof fail locally — agree with the accusation. Adopt + // the accuser's deadline so every voter on this accusation signs the + // same on-chain validity window. let mut vote = AccusationVote { e3_id: self.e3_id.clone(), accusation_id, voter: self.my_address, - agrees, data_hash: our_data_hash, + deadline: accusation.deadline, signature: ArcBytes::default(), }; - vote.signature = ArcBytes::from_bytes(&self.sign_vote_digest(&vote)); + match self.sign_vote_digest(&vote) { + Ok(sig) => vote.signature = ArcBytes::from_bytes(&sig), + Err(err) => { + error!("Failed to sign AccusationVote: {err}"); + return; + } + } info!( - "Voting {} on accusation against {} for {:?}", - if agrees { "AGREE" } else { "DISAGREE" }, - accusation.accused, - accusation.proof_type + "Agreeing with accusation against {} for {:?}", + accusation.accused, accusation.proof_type ); // Broadcast vote via gossip @@ -697,12 +1020,7 @@ impl AccusationManager { // Record in pending let pending = PendingAccusation { accusation, - votes_for: if agrees { - vec![vote.clone()] - } else { - Vec::new() - }, - votes_against: if agrees { Vec::new() } else { vec![vote] }, + votes_for: vec![vote], timeout_handle: Some(timeout_handle), ec: ec.clone(), }; @@ -766,6 +1084,18 @@ impl AccusationManager { return; }; + // Reject votes whose deadline disagrees with the accusation's chosen + // deadline. All voters must sign the same deadline so the aggregated + // evidence carries a single value for `SlashingManager`'s + // `block.timestamp <= deadline` check. + if vote.deadline != pending.accusation.deadline { + warn!( + "Ignoring vote from {} — deadline {} does not match accusation deadline {}", + vote.voter, vote.deadline, pending.accusation.deadline + ); + return; + } + // Reject votes from the accused party — they have a conflict of interest if vote.voter == pending.accusation.accused { warn!( @@ -776,11 +1106,7 @@ impl AccusationManager { } // Dedup: don't count same voter twice - let already_voted = pending - .votes_for - .iter() - .chain(pending.votes_against.iter()) - .any(|v| v.voter == vote.voter); + let already_voted = pending.votes_for.iter().any(|v| v.voter == vote.voter); if already_voted { return; } @@ -799,22 +1125,26 @@ impl AccusationManager { return; } - if vote.agrees { - pending.votes_for.push(vote); - } else { - pending.votes_against.push(vote); - } + // Every received `AccusationVote` is an agreement (the gossip wire + // carries no disagreement). Append to the agreeing pile and re-check + // quorum. + pending.votes_for.push(vote); - // Check if quorum reached self.check_quorum(vote_accusation_id, ec, ctx); } - /// Evaluate whether we have enough votes to decide. + /// Evaluate whether we have enough agreeing votes to decide. /// /// Quorum logic: - /// - Need >= M agreeing votes → AccusedFaulted - /// - If impossible to reach M even with remaining voters → early exit - /// - data_hash comparison detects equivocation vs false accusation + /// - `>= M` agreeing votes → `AccusedFaulted` (or `Equivocation` if those + /// votes disagree on `data_hash`, indicating the accused sent different + /// bytes to different peers). + /// - Otherwise → keep waiting; the timeout handler decides + /// `Inconclusive` if quorum never arrives. + /// + /// The gossip wire no longer carries disagreement, so there is no + /// fast-fail "quorum unreachable" branch — every silent peer might still + /// agree in flight. Silence beyond `vote_timeout` ⇒ `Inconclusive`. fn check_quorum( &mut self, accusation_id: [u8; 32], @@ -826,77 +1156,32 @@ impl AccusationManager { }; let agree_count = pending.votes_for.len(); - let disagree_count = pending.votes_against.len(); - let total_votes = agree_count + disagree_count; - - // CASE A: Majority says proof is bad → accused is at fault - // But first check for equivocation: if agreeing voters saw different data, - // the accused sent different payloads to different nodes. - if agree_count >= self.threshold_m { - let agree_hashes: HashSet<[u8; 32]> = - pending.votes_for.iter().map(|v| v.data_hash).collect(); - if agree_hashes.len() > 1 { - info!( - "Equivocation detected at quorum: {} unique data hashes among {} agreeing voters for {} {:?}", - agree_hashes.len(), - agree_count, - pending.accusation.accused, - pending.accusation.proof_type - ); - self.emit_quorum_reached(accusation_id, AccusationOutcome::Equivocation, ec, ctx); - } else { - info!( - "Quorum reached: {} votes confirm {} sent bad {:?} proof — AccusedFaulted", - agree_count, pending.accusation.accused, pending.accusation.proof_type - ); - self.emit_quorum_reached(accusation_id, AccusationOutcome::AccusedFaulted, ec, ctx); - } + if agree_count < self.threshold_m { + // Not yet at quorum — wait for more agreement votes or for the + // timeout to fire. return; } - // Check if quorum is still possible. - // Exclude the accused — they cannot vote on their own accusation. - let effective_committee = if self.committee.contains(&pending.accusation.accused) { - self.committee.len().saturating_sub(1) + // Reached `M` — decide between AccusedFaulted and Equivocation by + // checking whether the agreeing voters all saw the same data_hash. + let agree_hashes: HashSet<[u8; 32]> = + pending.votes_for.iter().map(|v| v.data_hash).collect(); + if agree_hashes.len() > 1 { + info!( + "Equivocation detected at quorum: {} unique data hashes among {} agreeing voters for {} {:?}", + agree_hashes.len(), + agree_count, + pending.accusation.accused, + pending.accusation.proof_type + ); + self.emit_quorum_reached(accusation_id, AccusationOutcome::Equivocation, ec, ctx); } else { - self.committee.len() - }; - let remaining = effective_committee.saturating_sub(total_votes); - if agree_count + remaining < self.threshold_m { - // Even if all remaining voters agree, can't reach quorum. - // Collect unique data hashes from actual votes only — do NOT include - // the accusation's data_hash because it is unverified (the accuser's - // own vote already carries their independently-observed hash). - let all_hashes: HashSet<[u8; 32]> = pending - .votes_for - .iter() - .chain(pending.votes_against.iter()) - .map(|v| v.data_hash) - .collect(); - - if all_hashes.len() > 1 { - // Different nodes received different data → equivocation by the accused - info!( - "Equivocation detected: {} unique data hashes for {} {:?}", - all_hashes.len(), - pending.accusation.accused, - pending.accusation.proof_type - ); - self.emit_quorum_reached(accusation_id, AccusationOutcome::Equivocation, ec, ctx); - } else if agree_count <= 1 && disagree_count > 0 { - // Same data, only accuser says bad, others say good → AccuserLied - info!( - "Accuser {} appears to have lied about {} {:?}", - pending.accusation.accuser, - pending.accusation.accused, - pending.accusation.proof_type - ); - self.emit_quorum_reached(accusation_id, AccusationOutcome::AccuserLied, ec, ctx); - } else { - self.emit_quorum_reached(accusation_id, AccusationOutcome::Inconclusive, ec, ctx); - } + info!( + "Quorum reached: {} votes confirm {} sent bad {:?} proof — AccusedFaulted", + agree_count, pending.accusation.accused, pending.accusation.proof_type + ); + self.emit_quorum_reached(accusation_id, AccusationOutcome::AccusedFaulted, ec, ctx); } - // Otherwise: still waiting for more votes — timeout will handle it } /// Called when the vote timeout expires for an accusation. @@ -905,19 +1190,10 @@ impl AccusationManager { return; // Already resolved }; - // Check for equivocation: if voters saw different data hashes, - // the accused sent different payloads to different nodes. - // Only use actual vote data_hashes — the accusation's data_hash is - // unverified and already represented by the accuser's own vote. - let all_hashes: HashSet<[u8; 32]> = pending - .votes_for - .iter() - .chain(pending.votes_against.iter()) - .map(|v| v.data_hash) - .collect(); - + // All votes received are agreements (the wire carries no + // disagreement signal). At timeout, decide between AccusedFaulted, + // Equivocation, or Inconclusive purely from the agreeing pile. let outcome = if pending.votes_for.len() >= self.threshold_m { - // Check among agreeing voters first let agree_hashes: HashSet<[u8; 32]> = pending.votes_for.iter().map(|v| v.data_hash).collect(); if agree_hashes.len() > 1 { @@ -925,22 +1201,26 @@ impl AccusationManager { } else { AccusationOutcome::AccusedFaulted } - } else if all_hashes.len() > 1 { - // Not enough votes to convict, but divergent data → equivocation - AccusationOutcome::Equivocation } else { + // Not enough agreements to convict and no signed disagreements + // exist; whether that's silence or active disagreement is + // indistinguishable on the wire. Report Inconclusive. AccusationOutcome::Inconclusive }; warn!( - "Accusation against {} for {:?} timed out with {} for / {} against — outcome: {:?}", + "Accusation against {} for {:?} timed out with {} agreeing votes — outcome: {:?}", pending.accusation.accused, pending.accusation.proof_type, pending.votes_for.len(), - pending.votes_against.len(), outcome ); + let evidence = self + .received_data + .get(&(pending.accusation.accused, pending.accusation.proof_type)) + .map(|d| d.evidence.clone()) + .unwrap_or_default(); if let Err(err) = self.bus.publish( AccusationQuorumReached { e3_id: self.e3_id.clone(), @@ -948,8 +1228,8 @@ impl AccusationManager { accused: pending.accusation.accused, proof_type: pending.accusation.proof_type, votes_for: pending.votes_for, - votes_against: pending.votes_against, outcome, + evidence, }, pending.ec, ) { @@ -974,14 +1254,18 @@ impl AccusationManager { } info!( - "Accusation quorum reached for {} {:?}: {} for, {} against — outcome: {}", + "Accusation quorum reached for {} {:?}: {} agreeing votes — outcome: {}", pending.accusation.accused, pending.accusation.proof_type, pending.votes_for.len(), - pending.votes_against.len(), outcome ); + let evidence = self + .received_data + .get(&(pending.accusation.accused, pending.accusation.proof_type)) + .map(|d| d.evidence.clone()) + .unwrap_or_default(); if let Err(err) = self.bus.publish( AccusationQuorumReached { e3_id: self.e3_id.clone(), @@ -989,8 +1273,8 @@ impl AccusationManager { accused: pending.accusation.accused, proof_type: pending.accusation.proof_type, votes_for: pending.votes_for, - votes_against: pending.votes_against, outcome, + evidence, }, ec.clone(), ) { @@ -1015,7 +1299,6 @@ impl AccusationManager { // Purge any votes from the expelled node in pending accusations for pending in self.pending.values_mut() { pending.votes_for.retain(|v| v.voter != data.operator); - pending.votes_against.retain(|v| v.voter != data.operator); } // Purge from buffered votes @@ -1033,12 +1316,14 @@ impl AccusationManager { proof_type: ProofType, data_hash: [u8; 32], passed: bool, + evidence: Bytes, ) { self.received_data.insert( (accused, proof_type), ReceivedProofData { data_hash, verification_passed: passed, + evidence, }, ); } @@ -1085,39 +1370,55 @@ impl AccusationManager { } }; - let agrees = !zk_passed; // ZK failed → proof is bad → agree with accusation - - // Cache the result for future accusations + // Cache the result for future accusations regardless of outcome. self.cache_verification_result( reverif.accused, reverif.proof_type, reverif.data_hash, zk_passed, + reverif.evidence.clone(), ); - // Get ec from the PendingAccusation - let ec = match self.pending.get(&reverif.accusation_id) { - Some(pending) => pending.ec.clone(), + // ZK re-verification passed ⇒ the proof is actually valid ⇒ we + // disagree with the accusation. The gossip wire carries no + // disagreement signal, so just abstain (no broadcast, no pending + // mutation). Other agreeing peers will or won't reach quorum + // independently. + if zk_passed { + info!( + "C3a/C3b re-verification passed for {:?} — abstaining from vote", + reverif.proof_type + ); + return; + } + + // ZK re-verification failed ⇒ we agree with the accusation. + let (ec, deadline) = match self.pending.get(&reverif.accusation_id) { + Some(pending) => (pending.ec.clone(), pending.accusation.deadline), None => { // Accusation already resolved (timeout/quorum) before ZK finished return; } }; - // Cast vote let mut vote = AccusationVote { e3_id: self.e3_id.clone(), accusation_id: reverif.accusation_id, voter: self.my_address, - agrees, data_hash: reverif.data_hash, + deadline, signature: ArcBytes::default(), }; - vote.signature = ArcBytes::from_bytes(&self.sign_vote_digest(&vote)); + match self.sign_vote_digest(&vote) { + Ok(sig) => vote.signature = ArcBytes::from_bytes(&sig), + Err(err) => { + error!("Failed to sign C3a/C3b AccusationVote: {err}"); + return; + } + } info!( - "C3a/C3b re-verification complete — voting {} on accusation against {:?}", - if agrees { "AGREE" } else { "DISAGREE" }, + "C3a/C3b re-verification confirmed failure for {:?} — agreeing with accusation", reverif.proof_type ); @@ -1128,11 +1429,7 @@ impl AccusationManager { // Record in pending if let Some(pending) = self.pending.get_mut(&reverif.accusation_id) { - if agrees { - pending.votes_for.push(vote); - } else { - pending.votes_against.push(vote); - } + pending.votes_for.push(vote); } // Check quorum @@ -1217,12 +1514,26 @@ impl Handler> for AccusationManager { _ctx: &mut Self::Context, ) -> Self::Result { let (data, _ec) = msg.into_components(); - // Cache successful verification for voting on future accusations + if data.e3_id != self.e3_id { + return; + } + if !self.committee.contains(&data.address) { + return; + } + // Cache successful verification for voting on future accusations. + // Evidence preimage = `abi.encode(proof.data, public_signals)`. + let evidence: Bytes = ( + Bytes::copy_from_slice(&data.proof_data), + Bytes::copy_from_slice(&data.public_signals), + ) + .abi_encode() + .into(); self.received_data.insert( (data.address, data.proof_type), ReceivedProofData { data_hash: data.data_hash, verification_passed: true, + evidence, }, ); } @@ -1286,3 +1597,213 @@ impl Handler> for AccusationManager { self.on_consistency_violation(data, &ec, ctx); } } + +// ════════════════════════════════════════════════════════════════════════════ +// Tests +// ════════════════════════════════════════════════════════════════════════════ +// +// These tests pin the actor's EIP-712 digest computation to the exact bytes +// that off-chain test helpers (and ultimately the on-chain +// `SlashingManager._verifyVotes`) expect. If anyone tweaks the typehash +// string, the domain name, or the struct field layout on EITHER side without +// updating the other, these tests fail before the broken signatures ever +// reach the chain. + +#[cfg(test)] +mod tests { + use super::*; + use alloy::primitives::FixedBytes; + use alloy::signers::SignerSync; + + /// Independent re-derivation of the EIP-712 vote digest, mirroring exactly + /// what `SlashingManager._verifyVotes` computes on chain. Kept here (and + /// not imported from a helper) so a regression in the actor's `vote_digest` + /// is caught by a byte-for-byte assertion against a hand-rolled reference. + fn reference_vote_digest( + chain_id: u64, + verifying_contract: Address, + e3_id: u64, + accusation_id: [u8; 32], + voter: Address, + data_hash: [u8; 32], + deadline: u64, + ) -> [u8; 32] { + let domain_typehash: [u8; 32] = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)", + ) + .into(); + let name_hash: [u8; 32] = keccak256(VOTE_DOMAIN_NAME).into(); + let version_hash: [u8; 32] = keccak256(VOTE_DOMAIN_VERSION).into(); + let domain_separator: [u8; 32] = keccak256( + &( + domain_typehash, + name_hash, + version_hash, + U256::from(chain_id), + verifying_contract, + ) + .abi_encode(), + ) + .into(); + + let typehash: [u8; 32] = keccak256(VOTE_TYPEHASH_STR).into(); + let struct_hash: [u8; 32] = keccak256( + &( + typehash, + U256::from(e3_id), + FixedBytes::<32>::from(accusation_id), + voter, + FixedBytes::<32>::from(data_hash), + U256::from(deadline), + ) + .abi_encode(), + ) + .into(); + + let mut buf = Vec::with_capacity(2 + 32 + 32); + buf.push(0x19); + buf.push(0x01); + buf.extend_from_slice(&domain_separator); + buf.extend_from_slice(&struct_hash); + keccak256(&buf).into() + } + + /// The actor's `vote_digest` must equal the reference digest byte-for-byte. + /// If this fails, the actor's typehash / domain / struct layout has drifted + /// from what the on-chain verifier expects (or from the constants in + /// `e3_events::accusation_vote`). + #[test] + fn vote_digest_matches_reference() { + let chain_id = 31337u64; + let verifying_contract: Address = "0x9999999999999999999999999999999999999999" + .parse() + .unwrap(); + let voter: Address = "0x2222222222222222222222222222222222222222" + .parse() + .unwrap(); + let accusation_id = [0xab; 32]; + let data_hash = [0xcd; 32]; + let deadline: u64 = 1_700_000_000; + + let vote = AccusationVote { + e3_id: E3id::new("42", chain_id), + accusation_id, + voter, + data_hash, + deadline, + signature: ArcBytes::default(), + }; + + let actor = AccusationManager::vote_digest(&vote, verifying_contract); + let reference = reference_vote_digest( + chain_id, + verifying_contract, + 42, + accusation_id, + voter, + data_hash, + deadline, + ); + + assert_eq!( + actor, reference, + "AccusationManager::vote_digest drifted from the reference EIP-712 \ + computation. Check VOTE_TYPEHASH_STR / VOTE_DOMAIN_NAME against \ + SlashingManager.sol — these MUST stay byte-equal across crates." + ); + } + + /// Sign-and-recover round-trip using the actor's digest. Since + /// `vote_digest_matches_reference` already pins the digest bytes, signing + /// that digest and recovering via `recover_address_from_prehash` must + /// return the voter — i.e. the actor's signatures will be accepted by the + /// on-chain `ECDSA.recover` step. + #[test] + fn actor_signature_recovers_to_voter() { + let signer: PrivateKeySigner = + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + .parse() + .unwrap(); + let voter = signer.address(); + let verifying_contract: Address = "0x5555555555555555555555555555555555555555" + .parse() + .unwrap(); + let chain_id = 31337u64; + + let vote = AccusationVote { + e3_id: E3id::new("12345", chain_id), + accusation_id: [0x07; 32], + voter, + data_hash: [0x08; 32], + deadline: 1_700_000_000, + signature: ArcBytes::default(), + }; + + let digest = AccusationManager::vote_digest(&vote, verifying_contract); + let sig = signer + .sign_hash_sync(&FixedBytes::<32>::from(digest)) + .unwrap(); + let recovered = sig + .recover_address_from_prehash(&FixedBytes::<32>::from(digest)) + .expect("recover"); + assert_eq!( + recovered, voter, + "signing the actor's digest and recovering must yield the voter" + ); + } + + /// The accusation digest must include `deadline`. A malicious peer could + /// otherwise rewrite the deadline in transit without invalidating the + /// accuser's signature. Guard: changing only `deadline` must change the + /// digest. + #[test] + fn accusation_digest_binds_deadline() { + let make = |deadline: u64| ProofFailureAccusation { + e3_id: E3id::new("9", 31337), + accuser: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + .parse() + .unwrap(), + accused: "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + .parse() + .unwrap(), + accused_party_id: 1, + proof_type: ProofType::C1PkGeneration, + data_hash: [0x42; 32], + deadline, + signed_payload: None, + signature: ArcBytes::default(), + }; + let a = AccusationManager::accusation_digest(&make(1_700_000_000)); + let b = AccusationManager::accusation_digest(&make(1_700_000_001)); + assert_ne!(a, b, "deadline must be part of the accusation digest"); + } + + #[test] + fn peer_deadline_acceptance_enforces_local_window() { + let now = 1_700_000_000u64; + let validity = 1_800u64; + let skew = DEFAULT_ACCUSATION_DEADLINE_SKEW_SECS; + let max_ok = now + validity + skew; + + assert!( + !AccusationManager::is_peer_deadline_acceptable(now, now, validity, skew), + "deadline equal to now must be rejected" + ); + assert!( + !AccusationManager::is_peer_deadline_acceptable(now - 1, now, validity, skew), + "expired deadline must be rejected" + ); + assert!( + AccusationManager::is_peer_deadline_acceptable(max_ok, now, validity, skew), + "deadline at upper bound must be accepted" + ); + assert!( + !AccusationManager::is_peer_deadline_acceptable(max_ok + 1, now, validity, skew), + "far-future deadline must be rejected" + ); + assert!( + !AccusationManager::is_peer_deadline_acceptable(now + 10, now, 0, skew), + "vote_validity_secs=0 must reject peer accusations" + ); + } +} diff --git a/crates/zk-prover/src/actors/accusation_manager_ext.rs b/crates/zk-prover/src/actors/accusation_manager_ext.rs index 9fce515c85..2e5c74568d 100644 --- a/crates/zk-prover/src/actors/accusation_manager_ext.rs +++ b/crates/zk-prover/src/actors/accusation_manager_ext.rs @@ -10,6 +10,8 @@ //! Listens for [`CommitteeFinalized`], reads `threshold_m` from [`E3Meta`], //! parses committee addresses, and starts the actor with full context. +use std::collections::HashMap; + use crate::AccusationManager; use alloy::primitives::Address; use alloy::signers::local::PrivateKeySigner; @@ -17,20 +19,52 @@ use anyhow::Result; use async_trait::async_trait; use e3_events::{BusHandle, CommitteeFinalized, EnclaveEvent, EnclaveEventData, Event}; use e3_request::{E3Context, E3ContextSnapshot, E3Extension, META_KEY}; -use tracing::{error, info}; +use tracing::{error, info, warn}; pub struct AccusationManagerExtension { bus: BusHandle, signer: PrivateKeySigner, + /// On-chain `SlashingManager` address (EIP-712 `verifyingContract` for vote sigs). + slashing_manager: Address, + /// Per-chain off-chain freshness window (seconds), read from + /// `CiphernodeRegistry.accusationVoteValidity()` at process startup. + /// Looked up by `e3_id.chain_id()` when each per-E3 actor starts; + /// governance changes require a node restart to take effect (same lifecycle + /// contract as `slashing_manager`). + vote_validity_secs_by_chain: HashMap, + /// Clock-skew allowance for peer accusation deadlines. + accusation_deadline_skew_secs: u64, } impl AccusationManagerExtension { - pub fn create(bus: &BusHandle, signer: PrivateKeySigner) -> Box { + pub fn create( + bus: &BusHandle, + signer: PrivateKeySigner, + slashing_manager: Address, + vote_validity_secs_by_chain: HashMap, + accusation_deadline_skew_secs: u64, + ) -> Box { Box::new(Self { bus: bus.clone(), signer: signer.clone(), + slashing_manager, + vote_validity_secs_by_chain, + accusation_deadline_skew_secs, }) } + + fn vote_validity_secs_for(&self, chain_id: u64) -> u64 { + match self.vote_validity_secs_by_chain.get(&chain_id) { + Some(&secs) => secs, + None => { + warn!( + chain_id, + "no accusationVoteValidity configured for chain; accusation votes will not be stamped" + ); + 0 + } + } + } } #[async_trait] @@ -83,12 +117,17 @@ impl E3Extension for AccusationManagerExtension { threshold_m ); + let vote_validity_secs = self.vote_validity_secs_for(e3_id.chain_id()); + let addr = AccusationManager::setup( &self.bus, e3_id, self.signer.clone(), + self.slashing_manager, committee_addresses, threshold_m, + vote_validity_secs, + self.accusation_deadline_skew_secs, meta.params_preset, ); diff --git a/crates/zk-prover/src/actors/commitment_consistency_checker.rs b/crates/zk-prover/src/actors/commitment_consistency_checker.rs index 1ce526ca18..73a707acdc 100644 --- a/crates/zk-prover/src/actors/commitment_consistency_checker.rs +++ b/crates/zk-prover/src/actors/commitment_consistency_checker.rs @@ -33,6 +33,7 @@ use super::commitment_links::{CommitmentLink, LinkScope}; use actix::{Actor, Addr, Context, Handler}; use alloy::primitives::Address; +use alloy::sol_types::SolValue; use e3_events::{ BusHandle, CommitmentConsistencyCheckComplete, CommitmentConsistencyCheckRequested, CommitmentConsistencyViolation, E3id, EnclaveEvent, EnclaveEventData, EventContext, @@ -50,6 +51,11 @@ struct VerifiedProofData { address: Address, public_signals: ArcBytes, data_hash: [u8; 32], + /// Raw `proof.data` bytes. Together with `public_signals` they form the + /// preimage `abi.encode(proof.data, public_signals)` of `data_hash` — + /// forwarded to slashing so the on-chain contract can verify the dataHash + /// bound in voter signatures. + proof_data: ArcBytes, } /// Describes a source entry whose commitments are inconsistent with a target. @@ -58,6 +64,11 @@ struct Mismatch { address: Address, proof_type: ProofType, data_hash: [u8; 32], + /// Same preimage as `VerifiedProofData.proof_data` paired with + /// `public_signals`. Carried from cache into the emitted violation so + /// downstream slashing can bind voter signatures to evidence bytes. + proof_data: ArcBytes, + public_signals: ArcBytes, } /// Per-E3 actor that enforces cross-circuit commitment consistency. @@ -138,6 +149,8 @@ impl CommitmentConsistencyChecker { address: *addr, proof_type: src_type, data_hash: src.data_hash, + proof_data: src.proof_data.clone(), + public_signals: src.public_signals.clone(), }); break; // one mismatch per source entry is enough } @@ -188,6 +201,8 @@ impl CommitmentConsistencyChecker { address: src.address, proof_type: src_type, data_hash: src.data_hash, + proof_data: src.proof_data.clone(), + public_signals: src.public_signals.clone(), }); } } @@ -235,6 +250,8 @@ impl CommitmentConsistencyChecker { address: src.address, proof_type: src_type, data_hash: src.data_hash, + proof_data: src.proof_data.clone(), + public_signals: src.public_signals.clone(), }); } } @@ -275,26 +292,32 @@ impl CommitmentConsistencyChecker { m.address, m.proof_type, ); - self.emit_violation(m.party_id, m.address, m.proof_type, m.data_hash, ec); + self.emit_violation(&m, ec); } } } /// Publish a [`CommitmentConsistencyViolation`] for the accusation pipeline. - fn emit_violation( - &self, - accused_party_id: u64, - accused_address: Address, - proof_type: ProofType, - data_hash: [u8; 32], - ec: &EventContext, - ) { + fn emit_violation(&self, m: &Mismatch, ec: &EventContext) { + // Evidence preimage = `abi.encode(proof.data, public_signals)`. The + // on-chain `SlashingManager.proposeSlash` recomputes `keccak256(evidence)` + // and requires it to equal each voter's signed `dataHash`. Without + // these bytes, slashing via the consistency-violation path would be + // gated by the evidence binding (safe but unable to slash). + let evidence = alloy::primitives::Bytes::from( + ( + alloy::primitives::Bytes::copy_from_slice(&m.proof_data), + alloy::primitives::Bytes::copy_from_slice(&m.public_signals), + ) + .abi_encode(), + ); let violation = CommitmentConsistencyViolation { e3_id: self.e3_id.clone(), - accused_party_id, - accused_address, - proof_type, - data_hash, + accused_party_id: m.party_id, + accused_address: m.address, + proof_type: m.proof_type, + data_hash: m.data_hash, + evidence, }; if let Err(err) = self.bus.publish(violation, ec.clone()) { error!("Failed to publish CommitmentConsistencyViolation: {err}"); @@ -340,6 +363,9 @@ impl Handler> for CommitmentConsistencyCheck _ctx: &mut Self::Context, ) -> Self::Result { let (data, ec) = msg.into_components(); + if data.e3_id != self.e3_id { + return; + } let proof_type = data.proof_type; let address = data.address; @@ -352,6 +378,7 @@ impl Handler> for CommitmentConsistencyCheck address, public_signals: data.public_signals, data_hash: data.data_hash, + proof_data: data.proof_data, }, ); @@ -368,12 +395,15 @@ impl Handler> for CommitmentCons _ctx: &mut Self::Context, ) -> Self::Result { let (data, ec) = msg.into_components(); + if data.e3_id != self.e3_id { + return; + } let mut inconsistent_parties = BTreeSet::new(); // Cache each party's proof data for link evaluation. for party in &data.party_proofs { - for (proof_type, public_signals, data_hash) in &party.proofs { + for (proof_type, public_signals, data_hash, proof_data) in &party.proofs { self.insert_verified( party.address, *proof_type, @@ -382,6 +412,7 @@ impl Handler> for CommitmentCons address: party.address, public_signals: public_signals.clone(), data_hash: *data_hash, + proof_data: proof_data.clone(), }, ); } @@ -401,7 +432,7 @@ impl Handler> for CommitmentCons m.address, ); inconsistent_parties.insert(m.party_id); - self.emit_violation(m.party_id, m.address, m.proof_type, m.data_hash, &ec); + self.emit_violation(&m, &ec); } } diff --git a/crates/zk-prover/src/actors/mod.rs b/crates/zk-prover/src/actors/mod.rs index 66e146185a..92c2d3953c 100644 --- a/crates/zk-prover/src/actors/mod.rs +++ b/crates/zk-prover/src/actors/mod.rs @@ -30,7 +30,7 @@ //! let signer = PrivateKeySigner::random(); //! //! // Setup all actors with proper separation of concerns -//! setup_zk_actors(&bus, &backend, signer); +//! setup_zk_actors(&bus, &backend, signer, HashMap::new()); //! ``` pub mod accusation_manager; @@ -56,8 +56,10 @@ pub use share_verification::ShareVerificationActor; pub use zk_actor::ZkActor; use actix::{Actor, Addr}; +use alloy::primitives::Address; use alloy::signers::local::PrivateKeySigner; use e3_events::BusHandle; +use std::collections::HashMap; use crate::ZkBackend; @@ -65,14 +67,23 @@ use crate::ZkBackend; /// /// Requires a `ZkBackend` for proof generation/verification and a /// `PrivateKeySigner` for signing proofs (fault attribution). -pub fn setup_zk_actors(bus: &BusHandle, backend: &ZkBackend, signer: PrivateKeySigner) -> ZkActors { +/// `dkg_fold_attestation_verifiers_by_chain` maps each enabled chain's id to +/// `CiphernodeRegistry.dkgFoldAttestationVerifier()` (EIP-712 `verifyingContract` +/// for fold attestations). Fetched at node startup when proof aggregation is enabled. +pub fn setup_zk_actors( + bus: &BusHandle, + backend: &ZkBackend, + signer: PrivateKeySigner, + dkg_fold_attestation_verifiers_by_chain: HashMap>, +) -> ZkActors { let zk_actor = ZkActor::new(backend).start(); let verifier = zk_actor.clone().recipient(); - let proof_request = ProofRequestActor::setup(bus, signer); + let proof_request = ProofRequestActor::setup(bus, signer.clone()); let proof_verification = ProofVerificationActor::setup(bus, verifier); let share_verification = ShareVerificationActor::setup(bus); - let node_proof_aggregator = NodeProofAggregator::setup(bus); + let node_proof_aggregator = + NodeProofAggregator::setup(bus, signer, dkg_fold_attestation_verifiers_by_chain); ZkActors { zk_actor, diff --git a/crates/zk-prover/src/actors/node_proof_aggregator.rs b/crates/zk-prover/src/actors/node_proof_aggregator.rs index 8fed4f7782..a45ca6a6a9 100644 --- a/crates/zk-prover/src/actors/node_proof_aggregator.rs +++ b/crates/zk-prover/src/actors/node_proof_aggregator.rs @@ -10,16 +10,21 @@ use std::collections::{BTreeMap, HashMap}; use actix::{Actor, Addr, Context, Handler}; +use alloy::primitives::Address; +use alloy::signers::local::PrivateKeySigner; use e3_events::{ BusHandle, ComputeRequest, ComputeRequestError, ComputeResponse, ComputeResponseKind, - CorrelationId, DKGInnerProofReady, DKGRecursiveAggregationComplete, E3id, EnclaveEvent, - EnclaveEventData, EventContext, EventPublisher, EventSubscriber, EventType, NodeDkgFoldRequest, - Proof, Sequenced, ShareEncryptionProofRequest, ThresholdSharePending, TypedEvent, ZkRequest, - ZkResponse, + CorrelationId, DKGInnerProofReady, DKGRecursiveAggregationComplete, DkgFoldAttestationPayload, + E3Failed, E3Stage, E3id, EnclaveEvent, EnclaveEventData, EventContext, EventPublisher, + EventSubscriber, EventType, FailureReason, NodeDkgFoldRequest, Proof, Sequenced, + ShareEncryptionProofRequest, SignedDkgFoldAttestation, ThresholdSharePending, TypedEvent, + ZkRequest, ZkResponse, }; use e3_fhe_params::build_pair_for_preset; use tracing::{error, info, warn}; +use crate::node_fold_public::extract_node_fold_agg_commits; + /// Metadata from [`ThresholdSharePending`] for slot indices and sizing. struct NodeDkgFoldMeta { party_id: u64, @@ -44,23 +49,37 @@ struct DkgProofCollectionState { /// Actor that collects DKG inner proofs and dispatches a single [`ZkRequest::NodeDkgFold`]. pub struct NodeProofAggregator { bus: BusHandle, + signer: PrivateKeySigner, + /// Per-chain `DkgFoldAttestationVerifier` address (EIP-712 `verifyingContract`). + /// Looked up by `e3_id.chain_id()` when signing fold attestations. + dkg_fold_attestation_verifiers_by_chain: HashMap>, states: HashMap, fold_correlation: HashMap, pending_inner_proofs: HashMap>, } impl NodeProofAggregator { - pub fn new(bus: &BusHandle) -> Self { + pub fn new( + bus: &BusHandle, + signer: PrivateKeySigner, + dkg_fold_attestation_verifiers_by_chain: HashMap>, + ) -> Self { Self { bus: bus.clone(), + signer, + dkg_fold_attestation_verifiers_by_chain, states: HashMap::new(), fold_correlation: HashMap::new(), pending_inner_proofs: HashMap::new(), } } - pub fn setup(bus: &BusHandle) -> Addr { - let addr = Self::new(bus).start(); + pub fn setup( + bus: &BusHandle, + signer: PrivateKeySigner, + dkg_fold_attestation_verifiers_by_chain: HashMap>, + ) -> Addr { + let addr = Self::new(bus, signer, dkg_fold_attestation_verifiers_by_chain).start(); bus.subscribe(EventType::ThresholdSharePending, addr.clone().into()); bus.subscribe(EventType::DKGInnerProofReady, addr.clone().into()); bus.subscribe(EventType::ComputeResponse, addr.clone().into()); @@ -83,6 +102,7 @@ impl NodeProofAggregator { e3_id: e3_id.clone(), party_id: msg.full_share.party_id, aggregated_proof: None, + fold_attestation: None, }, ec, ) { @@ -304,16 +324,100 @@ impl NodeProofAggregator { return; }; + let party_id = state.meta.party_id; + let committee_n = state.meta.committee_n; + let committee_h = committee_n; + let n_moduli = state.meta.n_moduli; + + let fold_attestation = match extract_node_fold_agg_commits( + &proof, + committee_n, + committee_h, + n_moduli, + ) { + Ok((extracted_party, commits)) => { + if extracted_party != party_id { + error!( + e3_id = %e3_id, + expected_party_id = party_id, + extracted_party_id = extracted_party, + "NodeFold public party_id does not match sortition party_id" + ); + None + } else if let Some(verifying_contract) = + self.dkg_fold_attestation_verifier_for(&e3_id) + { + let payload = DkgFoldAttestationPayload { + e3_id: e3_id.clone(), + verifying_contract, + party_id, + agg_commits: commits, + }; + match SignedDkgFoldAttestation::sign(payload, &self.signer) { + Ok(signed) => Some(signed), + Err(e) => { + error!( + e3_id = %e3_id, + party_id, + error = %e, + "failed to sign DkgFoldAttestation" + ); + None + } + } + } else { + error!( + e3_id = %e3_id, + party_id, + "NodeProofAggregator: cannot sign DkgFoldAttestation — CiphernodeRegistry.dkgFoldAttestationVerifier not configured" + ); + None + } + } + Err(e) => { + error!( + e3_id = %e3_id, + party_id, + error = %e, + "failed to extract sk_agg/esm_agg from NodeFold proof" + ); + None + } + }; + + if fold_attestation.is_none() { + error!( + e3_id = %e3_id, + party_id, + "NodeDkgFold succeeded but fold attestation missing — failing E3" + ); + if let Err(err) = self.bus.publish( + E3Failed { + e3_id: e3_id.clone(), + failed_at_stage: E3Stage::CommitteeFinalized, + reason: FailureReason::DKGInvalidShares, + }, + state.last_ec, + ) { + error!( + "NodeProofAggregator: failed to publish E3Failed for E3 {}: {err}", + e3_id + ); + } + return; + } + info!( "NodeProofAggregator: NodeDkgFold complete for E3 {} party {} — publishing DKGRecursiveAggregationComplete", - e3_id, state.meta.party_id + e3_id, party_id ); if let Err(err) = self.bus.publish( DKGRecursiveAggregationComplete { e3_id: e3_id.clone(), - party_id: state.meta.party_id, + party_id, aggregated_proof: Some(proof), + fold_attestation, }, state.last_ec, ) { @@ -401,6 +505,21 @@ impl Handler> for NodeProofAggregator { } impl NodeProofAggregator { + fn dkg_fold_attestation_verifier_for(&self, e3_id: &E3id) -> Option
{ + let chain_id = e3_id.chain_id(); + match self.dkg_fold_attestation_verifiers_by_chain.get(&chain_id) { + Some(Some(addr)) => Some(*addr), + Some(None) => None, + None => { + warn!( + chain_id, + "no dkgFoldAttestationVerifier configured for chain" + ); + None + } + } + } + fn handle_compute_response(&mut self, msg: TypedEvent) { let (msg, _ec) = msg.into_components(); if let ComputeResponseKind::Zk(ZkResponse::NodeDkgFold(resp)) = msg.response { @@ -418,21 +537,21 @@ impl NodeProofAggregator { ); let state = self.states.remove(&e3_id); warn!( - "NodeProofAggregator: E3 {} NodeDkgFold failed — publishing DKGRecursiveAggregationComplete(None)", + "NodeProofAggregator: E3 {} NodeDkgFold failed — publishing E3Failed", e3_id ); - if let Some(state) = state { + if let Some(_state) = state { if let Err(err) = self.bus.publish( - DKGRecursiveAggregationComplete { + E3Failed { e3_id: e3_id.clone(), - party_id: state.meta.party_id, - aggregated_proof: None, + failed_at_stage: E3Stage::CommitteeFinalized, + reason: FailureReason::DKGInvalidShares, }, ec, ) { error!( - "NodeProofAggregator: failed to publish DKGRecursiveAggregationComplete(None) for E3 {}: {err}", + "NodeProofAggregator: failed to publish E3Failed for E3 {}: {err}", e3_id ); } @@ -455,6 +574,12 @@ mod tests { EventContext::::from(data.into()).sequence(0) } + fn test_signer() -> PrivateKeySigner { + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + .parse() + .expect("test signer") + } + fn dummy_proof(seed: u8) -> Proof { Proof::new( CircuitName::PkAggregation, @@ -470,9 +595,9 @@ mod tests { } #[actix::test] - async fn node_dkg_fold_compute_error_emits_none_aggregation_result() -> Result<()> { + async fn node_dkg_fold_compute_error_emits_e3_failed() -> Result<()> { let (bus, _rng, _seed, _params, _crp, _errors, history) = get_common_setup(None)?; - let mut aggregator = NodeProofAggregator::new(&bus); + let mut aggregator = NodeProofAggregator::new(&bus, test_signer(), HashMap::new()); let e3_id = E3id::new("42", 1); let correlation_id = CorrelationId::new(); @@ -496,6 +621,7 @@ mod tests { e3_id: e3_id.clone(), party_id: 7, aggregated_proof: None, + fold_attestation: None, }), }, ); @@ -532,16 +658,17 @@ mod tests { e3_id: e3_id.clone(), party_id: 7, aggregated_proof: None, + fold_attestation: None, }), )); let event = next_event(&history).await?; assert!(matches!( event.into_data(), - EnclaveEventData::DKGRecursiveAggregationComplete(data) + EnclaveEventData::E3Failed(data) if data.e3_id == e3_id - && data.party_id == 7 - && data.aggregated_proof.is_none() + && data.failed_at_stage == E3Stage::CommitteeFinalized + && data.reason == FailureReason::DKGInvalidShares )); assert!(!aggregator.states.contains_key(&e3_id)); assert!(aggregator.fold_correlation.is_empty()); @@ -552,7 +679,7 @@ mod tests { #[actix::test] async fn early_inner_proof_is_prebuffered_until_collection_starts() -> Result<()> { let (bus, _rng, _seed, _params, _crp, _errors, history) = get_common_setup(None)?; - let mut aggregator = NodeProofAggregator::new(&bus); + let mut aggregator = NodeProofAggregator::new(&bus, test_signer(), HashMap::new()); let e3_id = E3id::new("43", 1); let early_proof = dummy_proof(10); @@ -596,6 +723,7 @@ mod tests { e3_id: e3_id.clone(), party_id: 7, aggregated_proof: None, + fold_attestation: None, }), ); diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index eaad01f604..d342a29a57 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -20,9 +20,9 @@ use e3_events::{ EventContext, EventPublisher, EventSubscriber, EventType, FailureReason, PkAggregationProofPending, PkAggregationProofRequest, PkAggregationProofSigned, PkBfvProofRequest, PkGenerationProofSigned, Proof, ProofPayload, ProofType, - ProofVerificationPassed, Sequenced, ShareDecryptionProofPending, SignedProofFailed, - SignedProofPayload, ThresholdShare, ThresholdShareCreated, ThresholdSharePending, TypedEvent, - ZkRequest, ZkResponse, + ProofVerificationPassed, Sequenced, ShareDecryptionProofPending, SignedProofPayload, + ThresholdShare, ThresholdShareCreated, ThresholdSharePending, TypedEvent, ZkRequest, + ZkResponse, }; use e3_utils::utility_types::ArcBytes; use e3_utils::NotifySync; @@ -1400,6 +1400,7 @@ impl ProofRequestActor { proof_type: ProofType::C0PkBfv, data_hash, public_signals: proof.public_signals.clone(), + proof_data: proof.data.clone(), }, ec.clone(), ) { diff --git a/crates/zk-prover/src/actors/proof_verification.rs b/crates/zk-prover/src/actors/proof_verification.rs index b93481af20..93651d379e 100644 --- a/crates/zk-prover/src/actors/proof_verification.rs +++ b/crates/zk-prover/src/actors/proof_verification.rs @@ -15,10 +15,10 @@ use actix::{Actor, Addr, AsyncContext, Context, Handler, Message, Recipient}; use alloy::primitives::{keccak256, Address, Bytes}; use alloy::sol_types::SolValue; use e3_events::{ - BusHandle, CiphernodeSelected, E3id, EnclaveEvent, EnclaveEventData, EncryptionKey, - EncryptionKeyCreated, EncryptionKeyReceived, EventContext, EventPublisher, EventSubscriber, - EventType, Proof, ProofType, ProofVerificationFailed, ProofVerificationPassed, Sequenced, - SignedProofFailed, SignedProofPayload, TypedEvent, + BusHandle, E3id, EnclaveEvent, EnclaveEventData, EncryptionKey, EncryptionKeyCreated, + EncryptionKeyReceived, EventContext, EventPublisher, EventSubscriber, EventType, Proof, + ProofType, ProofVerificationFailed, ProofVerificationPassed, Sequenced, SignedProofFailed, + SignedProofPayload, TypedEvent, }; use e3_fhe_params::BfvPreset; use e3_utils::NotifySync; @@ -272,6 +272,7 @@ impl Handler> for ProofVerificationActor { proof_type: ProofType::C0PkBfv, data_hash, public_signals: signed_payload.payload.proof.public_signals.clone(), + proof_data: signed_payload.payload.proof.data.clone(), }, ec, ) { diff --git a/crates/zk-prover/src/actors/share_verification.rs b/crates/zk-prover/src/actors/share_verification.rs index 584582dd1e..078802131b 100644 --- a/crates/zk-prover/src/actors/share_verification.rs +++ b/crates/zk-prover/src/actors/share_verification.rs @@ -90,6 +90,10 @@ struct PendingVerification { party_proof_hashes: HashMap>, /// Cached (proof_type, public_signals) per party — for commitment consistency checking. party_public_signals: HashMap>, + /// Parallel to `party_public_signals` — raw `proof.data` per (party, proof_type). + /// Needed by `ProofVerificationPassed` so downstream actors can forward + /// evidence bytes to the slashing contract. + party_proof_data: HashMap>, /// BFV preset for circuit artifact resolution. params_preset: e3_fhe_params::BfvPreset, } @@ -120,6 +124,10 @@ struct PendingConsistencyCheck { party_proof_hashes: HashMap>, /// (proof_type, public_signals) per party — for consistency & ZK. party_public_signals: HashMap>, + /// Parallel to `party_public_signals` — raw `proof.data` per (party, proof_type). + /// Needed by `ProofVerificationPassed` so downstream actors can forward + /// evidence bytes to the slashing contract. + party_proof_data: HashMap>, /// Original ECDSA-passed share proofs for ZK dispatch. /// Populated for ShareProofs / ThresholdDecryptionProofs / PkGenerationProofs. ecdsa_passed_share_proofs: Vec, @@ -295,6 +303,7 @@ impl ShareVerificationActor { // Compute proof hashes and public signals for ECDSA-passed parties let mut party_proof_hashes: HashMap> = HashMap::new(); let mut party_public_signals: HashMap> = HashMap::new(); + let mut party_raw_proof_data: HashMap> = HashMap::new(); for party in &ecdsa_passed_parties { let hashes: Vec<(ProofType, [u8; 32])> = party .signed_proofs() @@ -318,8 +327,14 @@ impl ShareVerificationActor { ) }) .collect(); + let datas: Vec<(ProofType, ArcBytes)> = party + .signed_proofs() + .iter() + .map(|signed| (signed.payload.proof_type, signed.payload.proof.data.clone())) + .collect(); party_proof_hashes.insert(party.party_id(), hashes); party_public_signals.insert(party.party_id(), signals); + party_raw_proof_data.insert(party.party_id(), datas); } // Build consistency check request @@ -335,10 +350,15 @@ impl ShareVerificationActor { .get(&party.party_id()) .cloned() .unwrap_or_default(); + let raw_datas = party_raw_proof_data + .get(&party.party_id()) + .cloned() + .unwrap_or_default(); let proofs = signals .into_iter() .zip(hashes) - .map(|((pt, ps), (_, dh))| (pt, ps, dh)) + .zip(raw_datas) + .map(|(((pt, ps), (_, dh)), (_, pd))| (pt, ps, dh, pd)) .collect(); PartyProofData { party_id: party.party_id(), @@ -361,6 +381,7 @@ impl ShareVerificationActor { party_addresses, party_proof_hashes, party_public_signals, + party_proof_data: party_raw_proof_data, ecdsa_passed_share_proofs: Vec::new(), ecdsa_passed_decryption_proofs: Vec::new(), params_preset, @@ -504,6 +525,11 @@ impl ShareVerificationActor { .into_iter() .filter(|(pid, _)| dispatched_party_ids.contains(pid)) .collect(); + let party_proof_data: HashMap> = pending + .party_proof_data + .into_iter() + .filter(|(pid, _)| dispatched_party_ids.contains(pid)) + .collect(); // Store pending ZK verification state. // All prior dishonest parties (pre_dishonest + ECDSA + consistency) are @@ -521,6 +547,7 @@ impl ShareVerificationActor { party_addresses, party_proof_hashes, party_public_signals, + party_proof_data, params_preset: pending.params_preset, }, ); @@ -707,11 +734,16 @@ impl ShareVerificationActor { .copied() .unwrap_or_default(); let signals = pending.party_public_signals.get(&result.sender_party_id); + let datas = pending.party_proof_data.get(&result.sender_party_id); for (i, &(proof_type, data_hash)) in hashes.iter().enumerate() { let public_signals = signals .and_then(|s| s.get(i)) .map(|(_, ps)| ps.clone()) .unwrap_or_default(); + let proof_data = datas + .and_then(|d| d.get(i)) + .map(|(_, pd)| pd.clone()) + .unwrap_or_default(); if let Err(err) = self.bus.publish( ProofVerificationPassed { e3_id: pending.e3_id.clone(), @@ -720,6 +752,7 @@ impl ShareVerificationActor { proof_type, data_hash, public_signals, + proof_data, }, pending.ec.clone(), ) { diff --git a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs index 87ef773a25..7c575d558e 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -20,6 +20,7 @@ use alloy::primitives::Address; use e3_events::{CircuitName, CircuitVariant, Proof}; use e3_fhe_params::BfvPreset; use serde::Serialize; +use std::time::Instant; fn proof_field_strings(proof: &Proof) -> Result, ZkError> { bytes_to_field_strings(proof.data.as_ref()) @@ -140,13 +141,35 @@ pub struct NodeDkgFoldInput<'a> { pub party_id: u64, } +/// Per-step prove wall time inside [`prove_node_dkg_fold`] (for benchmarks / audit reports). +#[derive(Clone, Debug, Serialize)] +pub struct FoldProveStepTiming { + pub step: String, + pub seconds: f64, +} + +/// Output of [`prove_node_dkg_fold`] including sub-step timings. +#[derive(Clone, Debug)] +pub struct NodeDkgFoldProveResult { + pub proof: Proof, + pub step_timings: Vec, +} + +fn push_step(timings: &mut Vec, step: &str, started: Instant) { + timings.push(FoldProveStepTiming { + step: step.to_string(), + seconds: started.elapsed().as_secs_f64(), + }); +} + /// Run C2abFold → C3 folds → C3abFold → C4abFold → NodeFold; returns a [`CircuitName::NodeFold`] proof. pub fn prove_node_dkg_fold( prover: &ZkProver, input: &NodeDkgFoldInput, e3_id: &str, artifacts_dir: &str, -) -> Result { +) -> Result { + let mut step_timings = Vec::with_capacity(6); let c2a_vk = vk::load_vk_artifacts( &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), CircuitName::SkShareComputation, @@ -166,6 +189,7 @@ pub fn prove_node_dkg_fold( c2a_key_hash: c2a_vk.key_hash.clone(), c2b_key_hash: c2b_vk.key_hash.clone(), }; + let t = Instant::now(); let c2ab_proof = build_and_prove_recursive_bin( prover, CircuitName::C2abFold, @@ -173,7 +197,9 @@ pub fn prove_node_dkg_fold( &format!("{e3_id}-c2ab"), artifacts_dir, )?; + push_step(&mut step_timings, "c2ab_fold", t); + let t = Instant::now(); let c3a_folded = generate_sequential_c3_fold( prover, input.c3a_inner_proofs, @@ -182,6 +208,9 @@ pub fn prove_node_dkg_fold( &format!("{e3_id}-c3a"), artifacts_dir, )?; + push_step(&mut step_timings, "c3a_fold", t); + + let t = Instant::now(); let c3b_folded = generate_sequential_c3_fold( prover, input.c3b_inner_proofs, @@ -190,6 +219,7 @@ pub fn prove_node_dkg_fold( &format!("{e3_id}-c3b"), artifacts_dir, )?; + push_step(&mut step_timings, "c3b_fold", t); let c3_fold_vk = vk::load_vk_artifacts( &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), @@ -205,6 +235,7 @@ pub fn prove_node_dkg_fold( c3a_key_hash: c3_fold_vk.key_hash.clone(), c3b_key_hash: c3_fold_vk.key_hash.clone(), }; + let t = Instant::now(); let c3ab_proof = build_and_prove_recursive_bin( prover, CircuitName::C3abFold, @@ -212,6 +243,7 @@ pub fn prove_node_dkg_fold( &format!("{e3_id}-c3ab"), artifacts_dir, )?; + push_step(&mut step_timings, "c3ab_fold", t); // C4a and C4b are both proofs of the same `DkgShareDecryption` circuit, so they share the // same VK. Load it once and clone into both witness slots. @@ -229,6 +261,7 @@ pub fn prove_node_dkg_fold( c4a_key_hash: c4_vk.key_hash.clone(), c4b_key_hash: c4_vk.key_hash.clone(), }; + let t = Instant::now(); let c4ab_proof = build_and_prove_recursive_bin( prover, CircuitName::C4abFold, @@ -236,6 +269,7 @@ pub fn prove_node_dkg_fold( &format!("{e3_id}-c4ab"), artifacts_dir, )?; + push_step(&mut step_timings, "c4ab_fold", t); let c0_vk = vk::load_vk_artifacts( &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), @@ -282,13 +316,20 @@ pub fn prove_node_dkg_fold( c4ab_key_hash: c4ab_fold_vk.key_hash, }; - build_and_prove_recursive_bin( + let t = Instant::now(); + let proof = build_and_prove_recursive_bin( prover, CircuitName::NodeFold, &nf, &format!("{e3_id}-nodefold"), artifacts_dir, - ) + )?; + push_step(&mut step_timings, "node_fold", t); + + Ok(NodeDkgFoldProveResult { + proof, + step_timings, + }) } /// Inputs for [`prove_dkg_aggregation`]. diff --git a/crates/zk-prover/src/lib.rs b/crates/zk-prover/src/lib.rs index 98ee6344a3..136eedd35c 100644 --- a/crates/zk-prover/src/lib.rs +++ b/crates/zk-prover/src/lib.rs @@ -9,6 +9,7 @@ mod backend; mod circuits; mod config; mod error; +mod node_fold_public; mod prover; pub mod test_utils; mod traits; @@ -25,13 +26,15 @@ pub use circuits::aggregation::c3_accumulator::generate_sequential_c3_fold; pub use circuits::aggregation::c6_accumulator::generate_sequential_c6_fold; pub use circuits::aggregation::node_dkg_fold::{ prove_decryption_aggregation_jobs, prove_dkg_aggregation, prove_node_dkg_fold, - DecryptionAggregationJob, DkgAggregationInput, NodeDkgFoldInput, + DecryptionAggregationJob, DkgAggregationInput, FoldProveStepTiming, NodeDkgFoldInput, + NodeDkgFoldProveResult, }; pub use circuits::aggregation::nodes_fold_accumulator::generate_sequential_nodes_fold; pub use config::{verify_checksum, BbTarget, CircuitInfo, VersionInfo, ZkConfig}; pub use e3_events::CircuitVariant; pub use e3_zk_helpers::circuits::dkg::pk::circuit::PkCircuit; pub use error::ZkError; +pub use node_fold_public::extract_node_fold_agg_commits; pub use prover::ZkProver; pub use traits::Provable; pub use witness::{input_map, CompiledCircuit, WitnessGenerator}; diff --git a/crates/zk-prover/src/node_fold_public.rs b/crates/zk-prover/src/node_fold_public.rs new file mode 100644 index 0000000000..6f83f87238 --- /dev/null +++ b/crates/zk-prover/src/node_fold_public.rs @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Public IO layout for [`CircuitName::NodeFold`] (must stay aligned with `node_fold/src/main.nr`). + +use crate::circuits::utils::bytes_to_field_strings; +use crate::error::ZkError; +use e3_events::{CircuitName, DkgFoldAggCommits, Proof}; + +/// Total public field count for `node_fold` at committee size `n`, honest `h`, threshold moduli `l`. +pub fn node_fold_public_field_count(n: usize, h: usize, l: usize) -> usize { + 11 + n + 2 * (n + h) * l +} + +fn field_hex_to_bytes32(field: &str) -> Result<[u8; 32], ZkError> { + let s = field.strip_prefix("0x").unwrap_or(field); + if s.len() > 64 { + return Err(ZkError::InvalidInput(format!( + "field hex too long for bytes32: {field}" + ))); + } + let mut out = [0u8; 32]; + let decoded = hex::decode(s).map_err(|e| ZkError::InvalidInput(e.to_string()))?; + let start = 32usize.saturating_sub(decoded.len()); + out[start..].copy_from_slice(&decoded); + Ok(out) +} + +fn field_hex_to_u64(field: &str) -> Result { + let s = field.strip_prefix("0x").unwrap_or(field); + let trimmed = s.trim_start_matches('0'); + let trimmed = if trimmed.is_empty() { "0" } else { trimmed }; + u64::from_str_radix(trimmed, 16).map_err(|e| ZkError::InvalidInput(e.to_string())) +} + +/// Read `party_id`, `sk_agg_commit`, and `esm_agg_commit` from a `NodeFold` proof. +pub fn extract_node_fold_agg_commits( + proof: &Proof, + committee_n: usize, + committee_h: usize, + n_moduli: usize, +) -> Result<(u64, DkgFoldAggCommits), ZkError> { + if proof.circuit != CircuitName::NodeFold { + return Err(ZkError::InvalidInput(format!( + "expected NodeFold proof, got {}", + proof.circuit + ))); + } + let fields = bytes_to_field_strings(proof.public_signals.as_ref())?; + let expected = node_fold_public_field_count(committee_n, committee_h, n_moduli); + if fields.len() != expected { + return Err(ZkError::InvalidInput(format!( + "NodeFold public field count {} != expected {} (n={committee_n}, h={committee_h}, l={n_moduli})", + fields.len(), + expected + ))); + } + let party_id = field_hex_to_u64(&fields[0])?; + let sk_agg_commit = field_hex_to_bytes32(&fields[fields.len() - 2])?; + let esm_agg_commit = field_hex_to_bytes32(&fields[fields.len() - 1])?; + Ok(( + party_id, + DkgFoldAggCommits { + sk_agg_commit, + esm_agg_commit, + }, + )) +} + +#[cfg(test)] +mod tests { + use super::*; + use e3_utils::ArcBytes; + + #[test] + fn extracts_expected_fields_from_golden_layout_vector() { + // Golden layout vector: verifies positional extraction + // (party_id at index 0, sk/esm commits at tail). + let n = 3usize; + let h = 3usize; + let l = 2usize; + let field_count = node_fold_public_field_count(n, h, l); + + let mut fields = vec![[0u8; 32]; field_count]; + fields[0][31] = 2; // party_id = 2 + fields[field_count - 2] = [0x11; 32]; + fields[field_count - 1] = [0x22; 32]; + + let mut public_signals = Vec::with_capacity(field_count * 32); + for f in fields { + public_signals.extend_from_slice(&f); + } + + let proof = Proof::new( + CircuitName::NodeFold, + ArcBytes::from_bytes(&[]), + ArcBytes::from_bytes(&public_signals), + ); + + let (party_id, commits) = + extract_node_fold_agg_commits(&proof, n, h, l).expect("extract should succeed"); + assert_eq!(party_id, 2); + assert_eq!(commits.sk_agg_commit, [0x11; 32]); + assert_eq!(commits.esm_agg_commit, [0x22; 32]); + } +} diff --git a/crates/zk-prover/src/prover.rs b/crates/zk-prover/src/prover.rs index 262e8fe027..49fa0cead5 100644 --- a/crates/zk-prover/src/prover.rs +++ b/crates/zk-prover/src/prover.rs @@ -11,8 +11,18 @@ use e3_utils::utility_types::ArcBytes; use std::fs; use std::path::PathBuf; use std::process::Command as StdCommand; +use std::sync::atomic::{AtomicU64, Ordering}; use tracing::{debug, info, warn}; +/// Unique bb job directories — shared [`ZkBackend::work_dir`] must not reuse the same paths +/// when prove/verify runs concurrently (integration harness + `multithread_concurrent_jobs` > 1). +static BB_WORK_JOB_COUNTER: AtomicU64 = AtomicU64::new(0); + +fn next_bb_work_subdir(prefix: &str) -> String { + let id = BB_WORK_JOB_COUNTER.fetch_add(1, Ordering::Relaxed); + format!("{prefix}_{id}") +} + pub struct ZkProver { bb_binary: PathBuf, circuits_dir: PathBuf, @@ -160,7 +170,10 @@ impl ZkProver { ))); } - let job_dir = self.work_dir.join(e3_id); + let job_dir = self + .work_dir + .join(e3_id) + .join(next_bb_work_subdir(&format!("prove_{}", circuit.as_str()))); let witness_path = job_dir.join("witness.gz"); let output_dir = job_dir.join("out"); fs::create_dir_all(&job_dir)?; @@ -323,8 +336,6 @@ impl ZkProver { ))); } - let verification_subdir = format!("verify_party_{}", party_id); - debug!( "verifying proof for circuit {} (party {}) using VK: {}", circuit.as_str(), @@ -332,7 +343,10 @@ impl ZkProver { vk_path.display() ); - let job_dir = self.work_dir.join(e3_id).join(&verification_subdir); + let job_dir = self.work_dir.join(e3_id).join(next_bb_work_subdir(&format!( + "verify_party_{party_id}_{}", + circuit.as_str() + ))); let out_dir = job_dir.join("out"); fs::create_dir_all(&out_dir)?; diff --git a/crates/zk-prover/src/witness.rs b/crates/zk-prover/src/witness.rs index 60cf859897..ad09658dae 100644 --- a/crates/zk-prover/src/witness.rs +++ b/crates/zk-prover/src/witness.rs @@ -134,7 +134,7 @@ where mod tests { use super::*; - const DUMMY_CIRCUIT: &str = r#"{"noir_version":"1.0.0-beta.15+83245db91dcf63420ef4bcbbd85b98f397fee663","hash":"15412581843239610929","abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"},{"name":"_sum","type":{"kind":"field"},"visibility":"public"}],"return_type":null,"error_types":{}},"bytecode":"H4sIAAAAAAAA/5WOMQ5AMBRA/y8HMbIRRxCJSYwWg8RiIGIz9gjiAk4hHKeb0WLX0KHRDu1bXvL/y89H+HCFu7rtCTeCiiPsgRFo06LUhk0+smgN9iLdKC0rPz6z6RjmhN3LxffE/O7byg+hZv7nAb2HRPkUAQAA","debug_symbols":"jZDRCoMwDEX/Jc996MbG1F8ZQ2qNUghtie1giP++KLrpw2BPaXJ7bsgdocUm97XzXRiguo/QsCNyfU3BmuSCl+k4KdjaOjGijGCnCxUNo09Q+Uyk4GkoL5+GaPxSk2FRtQL0rVQx7Bzh/JrUl9a/0Vu5ssXlA1//psvbSp90ccAf0hnr+HAuaKjO0+zGzjSEawRd9naXSHrFTdkyixwstplxtls0WfAG","file_map":{"50":{"source":"pub fn main(\n x: Field,\n y: Field,\n _sum: pub Field\n) {\n let sum = x + y;\n assert(sum == _sum);\n}\n","path":"/Users/ctrlc03/Documents/zk/enclave/circuits/bin/dummy/src/main.nr"}},"expression_width":{"Bounded":{"width":4}}}"#; + const DUMMY_CIRCUIT: &str = include_str!("../tests/fixtures/dummy.json"); #[test] fn test_load_circuit() { diff --git a/crates/zk-prover/tests/fixtures/dummy.json b/crates/zk-prover/tests/fixtures/dummy.json new file mode 100644 index 0000000000..637f61556c --- /dev/null +++ b/crates/zk-prover/tests/fixtures/dummy.json @@ -0,0 +1,22 @@ +{ + "noir_version": "1.0.0-beta.15+83245db91dcf63420ef4bcbbd85b98f397fee663", + "hash": "15412581843239610929", + "abi": { + "parameters": [ + { "name": "x", "type": { "kind": "field" }, "visibility": "private" }, + { "name": "y", "type": { "kind": "field" }, "visibility": "private" }, + { "name": "_sum", "type": { "kind": "field" }, "visibility": "public" } + ], + "return_type": null, + "error_types": {} + }, + "bytecode": "H4sIAAAAAAAA/5WOMQ5AMBRA/y8HMbIRRxCJSYwWg8RiIGIz9gjiAk4hHKeb0WLX0KHRDu1bXvL/y89H+HCFu7rtCTeCiiPsgRFo06LUhk0+smgN9iLdKC0rPz6z6RjmhN3LxffE/O7byg+hZv7nAb2HRPkUAQAA", + "debug_symbols": "jZDRCoMwDEX/Jc996MbG1F8ZQ2qNUghtie1giP++KLrpw2BPaXJ7bsgdocUm97XzXRiguo/QsCNyfU3BmuSCl+k4KdjaOjGijGCnCxUNo09Q+Uyk4GkoL5+GaPxSk2FRtQL0rVQx7Bzh/JrUl9a/0Vu5ssXlA1//psvbSp90ccAf0hnr+HAuaKjO0+zGzjSEawRd9naXSHrFTdkyixwstplxtls0WfAG", + "file_map": { + "50": { + "source": "pub fn main(\n x: Field,\n y: Field,\n _sum: pub Field\n) {\n let sum = x + y;\n assert(sum == _sum);\n}\n", + "path": "circuits/bin/dummy/src/main.nr" + } + }, + "expression_width": { "Bounded": { "width": 4 } } +} diff --git a/crates/zk-prover/tests/slashing_integration_tests.rs b/crates/zk-prover/tests/slashing_integration_tests.rs index 3d84c94d15..a47077df20 100644 --- a/crates/zk-prover/tests/slashing_integration_tests.rs +++ b/crates/zk-prover/tests/slashing_integration_tests.rs @@ -70,7 +70,8 @@ sol! { uint8 failureReason; } - function proposeSlash(uint256 e3Id, address operator, bytes32 reason, bytes calldata proof) external returns (uint256 proposalId); + function proposeSlash(uint256 e3Id, address operator, bytes calldata proof) external returns (uint256 proposalId); + function getSlashPolicy(bytes32 reason) external view returns (SlashPolicy memory); function setSlashPolicy(bytes32 reason, SlashPolicy calldata policy) external; function setBondingRegistry(address newBondingRegistry) external; function setCiphernodeRegistry(address newCiphernodeRegistry) external; @@ -91,6 +92,7 @@ sol! { function setCommitteeNodes(uint256 e3Id, address[] calldata nodes) external; function setThreshold(uint256 e3Id, uint32 m) external; } + } // ── Helpers ── @@ -367,10 +369,35 @@ fn test_digest_matches_solidity_encoding() { // ════════════════════════════════════════════════════════════════════════════ // Attestation vote helpers — used by both pure Rust and on-chain tests +// +// The vote typehash / domain name / domain version are imported from +// `e3_events` so the test helper and the production `AccusationManager` actor +// always hash the SAME bytes. Adding a fourth source of truth here would +// reintroduce exactly the drift class this test layout exists to prevent. // ════════════════════════════════════════════════════════════════════════════ -const VOTE_TYPEHASH_STR: &str = - "AccusationVote(uint256 chainId,uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)"; +use e3_events::{VOTE_DOMAIN_NAME, VOTE_DOMAIN_VERSION, VOTE_TYPEHASH_STR}; + +const VOTE_DOMAIN_TYPEHASH_STR: &str = + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"; +/// Sentinel deadline matching Hardhat `ethers.MaxUint256` (no expiry in tests). +const VOTE_NO_EXPIRY: U256 = U256::MAX; + +/// Lane A policy key: `keccak256(abi.encodePacked(proofType))` (must match `SlashingManager.proposeSlash`). +fn reason_for_proof_type(proof_type: u8) -> FixedBytes<32> { + keccak256(&U256::from(proof_type).abi_encode_packed()).into() +} + +/// Custom error selectors from `SlashingManager` (Anvil returns selector, not name). +fn err_has_selector(err: &str, selector: &str) -> bool { + err.contains(selector) || err.contains(selector.trim_start_matches("0x")) +} + +const SEL_INSUFFICIENT_ATTESTATIONS: &str = "0xe424f994"; +const SEL_DUPLICATE_VOTER: &str = "0xcbceb64b"; +const SEL_VOTER_NOT_IN_COMMITTEE: &str = "0x4ca81c26"; +const SEL_INVALID_VOTE_SIGNATURE: &str = "0x64a283db"; +const SEL_DUPLICATE_EVIDENCE: &str = "0x5be07e5e"; /// Compute `accusationId = keccak256(abi.encodePacked(chainId, e3Id, operator, proofType))` /// matching `AccusationManager::accusation_id()` and `SlashingManager._verifyAttestationEvidence()`. @@ -391,75 +418,143 @@ fn compute_accusation_id( ) } -/// Compute the structured vote digest matching `AccusationManager::vote_digest()`. +/// Compute the canonical EIP-712 vote domain separator. +fn compute_vote_domain_separator(chain_id: u64, verifying_contract: Address) -> FixedBytes<32> { + let domain_typehash = keccak256(VOTE_DOMAIN_TYPEHASH_STR); + let name_hash = keccak256(VOTE_DOMAIN_NAME.as_bytes()); + let version_hash = keccak256(VOTE_DOMAIN_VERSION.as_bytes()); + keccak256( + &( + domain_typehash, + name_hash, + version_hash, + U256::from(chain_id), + verifying_contract, + ) + .abi_encode(), + ) +} + +/// Compute the canonical EIP-712 typed-data hash for a vote, matching +/// `AccusationManager::vote_digest()` and `SlashingManager._verifyVotes`. fn compute_vote_digest( chain_id: u64, + verifying_contract: Address, e3_id: u64, accusation_id: FixedBytes<32>, voter: Address, - agrees: bool, data_hash: FixedBytes<32>, + deadline: U256, ) -> FixedBytes<32> { let typehash = keccak256(VOTE_TYPEHASH_STR); - keccak256( + let struct_hash = keccak256( &( typehash, - U256::from(chain_id), U256::from(e3_id), accusation_id, voter, - agrees, data_hash, + deadline, ) .abi_encode(), - ) + ); + let domain = compute_vote_domain_separator(chain_id, verifying_contract); + let mut buf = Vec::with_capacity(2 + 32 + 32); + buf.push(0x19); + buf.push(0x01); + buf.extend_from_slice(domain.as_ref()); + buf.extend_from_slice(struct_hash.as_ref()); + keccak256(&buf) } -/// Sign a vote and return `(voter_address, signature_bytes)`. +/// Sign a vote and return `(voter_address, signature_bytes)`. EIP-712 typed-data signature. fn sign_vote( signer: &PrivateKeySigner, chain_id: u64, + verifying_contract: Address, + e3_id: u64, + accusation_id: FixedBytes<32>, + data_hash: FixedBytes<32>, +) -> (Address, Bytes) { + sign_vote_with_deadline( + signer, + chain_id, + verifying_contract, + e3_id, + accusation_id, + data_hash, + VOTE_NO_EXPIRY, + ) +} + +fn sign_vote_with_deadline( + signer: &PrivateKeySigner, + chain_id: u64, + verifying_contract: Address, e3_id: u64, accusation_id: FixedBytes<32>, - agrees: bool, data_hash: FixedBytes<32>, + deadline: U256, ) -> (Address, Bytes) { let voter = signer.address(); - let digest = compute_vote_digest(chain_id, e3_id, accusation_id, voter, agrees, data_hash); + let digest = compute_vote_digest( + chain_id, + verifying_contract, + e3_id, + accusation_id, + voter, + data_hash, + deadline, + ); + // EIP-712: sign the typed-data hash directly (no EIP-191 wrapping). let sig = signer - .sign_message_sync(digest.as_ref()) + .sign_hash_sync(&digest) .expect("vote signing should succeed"); (voter, Bytes::from(sig.as_bytes().to_vec())) } /// Encode attestation evidence for `proposeSlash()`. /// -/// Format: `abi.encode(uint256 proofType, address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures)` -/// Voters are sorted ascending by address (contract requires strict ascending order). +/// Format: `abi.encode(uint256 proofType, address[] voters, bytes32[] dataHashes, +/// uint256 deadline, bytes[] signatures)`. Voters are sorted ascending by address. fn encode_attestation_evidence( proof_type: u8, - mut votes: Vec<(Address, bool, FixedBytes<32>, Bytes)>, + mut votes: Vec<(Address, FixedBytes<32>, Bytes)>, + deadline: U256, ) -> Bytes { - votes.sort_by_key(|(addr, _, _, _)| *addr); + votes.sort_by_key(|(addr, _, _)| *addr); - let voters: Vec
= votes.iter().map(|(a, _, _, _)| *a).collect(); - let agrees: Vec = votes.iter().map(|(_, a, _, _)| *a).collect(); - let data_hashes: Vec> = votes.iter().map(|(_, _, d, _)| *d).collect(); - let sigs: Vec = votes.iter().map(|(_, _, _, s)| s.clone()).collect(); + let voters: Vec
= votes.iter().map(|(a, _, _)| *a).collect(); + let data_hashes: Vec> = votes.iter().map(|(_, d, _)| *d).collect(); + let sigs: Vec = votes.iter().map(|(_, _, s)| s.clone()).collect(); - Bytes::from((U256::from(proof_type), voters, agrees, data_hashes, sigs).abi_encode()) + // `abi_encode_params` matches Solidity `abi.encode(a,b,...)`; `abi_encode` adds an extra + // outer offset word that breaks `abi.decode(proof, (uint256))` in `proposeSlash`. + (U256::from(proof_type), voters, data_hashes, deadline, sigs) + .abi_encode_params() + .into() } // ════════════════════════════════════════════════════════════════════════════ // Pure Rust attestation tests — no Anvil required // ════════════════════════════════════════════════════════════════════════════ +/// Lane A reason key must match Hardhat `REASON_PT_0` / `keccak256(solidityPacked(uint256, 0))`. +#[test] +fn test_reason_for_proof_type_matches_solidity() { + let expected: FixedBytes<32> = + "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563" + .parse() + .unwrap(); + assert_eq!(reason_for_proof_type(0), expected); +} + /// Verifies the VOTE_TYPEHASH constant matches the keccak256 of the vote type string. #[test] fn test_vote_typehash() { let expected: [u8; 32] = keccak256(VOTE_TYPEHASH_STR).into(); // Cross-check with the exact string the Solidity contract uses: - let sol_str = "AccusationVote(uint256 chainId,uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)"; + let sol_str = "AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bytes32 dataHash,uint256 deadline)"; let sol_hash: [u8; 32] = keccak256(sol_str).into(); assert_eq!( expected, sol_hash, @@ -467,10 +562,13 @@ fn test_vote_typehash() { ); } -/// Verifies vote digest computation matches manual abi.encode + keccak256. +/// Verifies vote digest computation matches the canonical EIP-712 typed-data hash. #[test] fn test_vote_digest_manual_computation() { let chain_id = 31337u64; + let verifying_contract: Address = "0x9999999999999999999999999999999999999999" + .parse() + .unwrap(); let e3_id = 42u64; let operator: Address = "0x1111111111111111111111111111111111111111" .parse() @@ -482,29 +580,44 @@ fn test_vote_digest_manual_computation() { let data_hash = FixedBytes::from([0xab; 32]); let accusation_id = compute_accusation_id(chain_id, e3_id, operator, proof_type); - let digest = compute_vote_digest(chain_id, e3_id, accusation_id, voter, true, data_hash); - - // Manual computation - let typehash = keccak256(VOTE_TYPEHASH_STR); - let encoded = ( - typehash, - U256::from(chain_id), - U256::from(e3_id), + let digest = compute_vote_digest( + chain_id, + verifying_contract, + e3_id, accusation_id, voter, - true, data_hash, - ) - .abi_encode(); - let expected: FixedBytes<32> = keccak256(&encoded); + VOTE_NO_EXPIRY, + ); + + // Manual EIP-712 computation + let typehash = keccak256(VOTE_TYPEHASH_STR); + let struct_hash: FixedBytes<32> = keccak256( + &( + typehash, + U256::from(e3_id), + accusation_id, + voter, + data_hash, + VOTE_NO_EXPIRY, + ) + .abi_encode(), + ); + let domain = compute_vote_domain_separator(chain_id, verifying_contract); + let mut buf = Vec::with_capacity(2 + 32 + 32); + buf.push(0x19); + buf.push(0x01); + buf.extend_from_slice(domain.as_ref()); + buf.extend_from_slice(struct_hash.as_ref()); + let expected: FixedBytes<32> = keccak256(&buf); assert_eq!( digest, expected, - "vote digest should match manual computation" + "vote digest should match canonical EIP-712 typed-data hash" ); } -/// Verifies vote sign/recover roundtrip. +/// Verifies vote sign/recover roundtrip (EIP-712, no EIP-191 wrapping). #[test] fn test_vote_signing_roundtrip() { let signer: PrivateKeySigner = @@ -512,6 +625,9 @@ fn test_vote_signing_roundtrip() { .parse() .unwrap(); let chain_id = 31337u64; + let verifying_contract: Address = "0x9999999999999999999999999999999999999999" + .parse() + .unwrap(); let e3_id = 42u64; let operator: Address = "0x1111111111111111111111111111111111111111" .parse() @@ -520,7 +636,14 @@ fn test_vote_signing_roundtrip() { let data_hash = FixedBytes::from([0xab; 32]); let accusation_id = compute_accusation_id(chain_id, e3_id, operator, proof_type); - let (voter, sig_bytes) = sign_vote(&signer, chain_id, e3_id, accusation_id, true, data_hash); + let (voter, sig_bytes) = sign_vote( + &signer, + chain_id, + verifying_contract, + e3_id, + accusation_id, + data_hash, + ); assert_eq!( voter, @@ -528,12 +651,20 @@ fn test_vote_signing_roundtrip() { "voter should be the signer address" ); - // Verify recover - let digest = compute_vote_digest(chain_id, e3_id, accusation_id, voter, true, data_hash); + // Verify recover (raw prehash, no EIP-191 wrapping) + let digest = compute_vote_digest( + chain_id, + verifying_contract, + e3_id, + accusation_id, + voter, + data_hash, + VOTE_NO_EXPIRY, + ); let sig = alloy::primitives::Signature::try_from(sig_bytes.as_ref()).expect("signature should parse"); let recovered = sig - .recover_address_from_msg(digest.as_slice()) + .recover_address_from_prehash(&digest) .expect("recovery should succeed"); assert_eq!( recovered, @@ -542,6 +673,35 @@ fn test_vote_signing_roundtrip() { ); } +/// First ABI word of attestation evidence must be `proofType` (SlashingManager decodes only that). +#[test] +fn test_evidence_leading_word_is_proof_type() { + let evidence = encode_attestation_evidence( + 0, + vec![ + ( + "0x1111111111111111111111111111111111111111" + .parse() + .unwrap(), + FixedBytes::from([1u8; 32]), + Bytes::from(vec![0u8; 65]), + ), + ( + "0x2222222222222222222222222222222222222222" + .parse() + .unwrap(), + FixedBytes::from([2u8; 32]), + Bytes::from(vec![0u8; 65]), + ), + ], + VOTE_NO_EXPIRY, + ); + let leading = U256::from_be_slice(&evidence[..32]); + assert_eq!(leading, U256::ZERO, "leading word must be proofType"); + let derived_reason: FixedBytes<32> = keccak256(&leading.abi_encode_packed()).into(); + assert_eq!(derived_reason, reason_for_proof_type(0)); +} + /// Verifies attestation evidence encoding structure. #[test] fn test_attestation_evidence_encoding() { @@ -549,47 +709,60 @@ fn test_attestation_evidence_encoding() { let signer2: PrivateKeySigner = PrivateKeySigner::random(); let chain_id = 31337u64; + let verifying_contract: Address = "0x9999999999999999999999999999999999999999" + .parse() + .unwrap(); let e3_id = 1u64; let operator: Address = "0x1111111111111111111111111111111111111111" .parse() .unwrap(); let proof_type = 0u8; - let data_hash = FixedBytes::from([0xcc; 32]); let accusation_id = compute_accusation_id(chain_id, e3_id, operator, proof_type); - let (voter1, sig1) = sign_vote(&signer1, chain_id, e3_id, accusation_id, true, data_hash); - let (voter2, sig2) = sign_vote(&signer2, chain_id, e3_id, accusation_id, true, data_hash); + let data_hash = FixedBytes::from([0xab; 32]); + let (voter1, sig1) = sign_vote( + &signer1, + chain_id, + verifying_contract, + e3_id, + accusation_id, + data_hash, + ); + let (voter2, sig2) = sign_vote( + &signer2, + chain_id, + verifying_contract, + e3_id, + accusation_id, + data_hash, + ); let evidence = encode_attestation_evidence( proof_type, - vec![ - (voter1, true, data_hash, sig1), - (voter2, true, data_hash, sig2), - ], + vec![(voter1, data_hash, sig1), (voter2, data_hash, sig2)], + VOTE_NO_EXPIRY, ); - // Decode and verify structure: (uint256, address[], bool[], bytes32[], bytes[]) - type AttestationTuple = ( - U256, - Vec
, - Vec, - Vec>, - Vec, - ); + // Decode and verify structure: (uint256, address[], bytes32[], uint256, bytes[]) + type AttestationTuple = (U256, Vec
, Vec>, U256, Vec); let decoded = AttestationTuple::abi_decode_params(&evidence).expect("evidence should ABI-decode"); - let (dec_proof_type, dec_voters, dec_agrees, dec_hashes, dec_sigs) = decoded; + let (dec_proof_type, dec_voters, dec_hashes, dec_deadline, dec_sigs) = decoded; assert_eq!(dec_proof_type, U256::from(proof_type), "proofType mismatch"); assert_eq!(dec_voters.len(), 2, "should have 2 voters"); assert!( dec_voters[0] < dec_voters[1], "voters should be sorted ascending" ); - assert!(dec_agrees.iter().all(|a| *a), "all votes should agree"); assert_eq!(dec_hashes.len(), 2, "should have 2 data hashes"); + assert_eq!(dec_deadline, VOTE_NO_EXPIRY, "deadline mismatch"); assert_eq!(dec_sigs.len(), 2, "should have 2 signatures"); + assert!( + dec_hashes.iter().all(|h| *h == data_hash), + "all voters must share the same dataHash" + ); } // ════════════════════════════════════════════════════════════════════════════ @@ -611,8 +784,8 @@ async fn deploy_and_configure( // Deploy returner for bondingRegistry (slashTicketBalance returns uint256) let returner_addr = deploy_contract(provider, RETURNER_DEPLOY_BYTECODE, &[]).await; - // Deploy SlashingManager(admin) — constructor only takes admin address - let sm_args = admin.abi_encode(); + // Deploy SlashingManager(initialDelay, admin) — use 0 delay for local tests + let sm_args = (0u64, admin).abi_encode(); let sm_addr = deploy_contract(provider, sm_bytecode, &sm_args).await; // Configure dependencies via admin functions @@ -688,9 +861,9 @@ async fn test_onchain_valid_attestation_executes_slash() { let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; let slashing_mgr = SlashingManager::new(sm_addr, &provider); - let reason: FixedBytes<32> = keccak256("E3_BAD_DKG_PROOF"); let e3_id: u64 = 42; let proof_type = 0u8; // C0PkBfv + let reason = reason_for_proof_type(proof_type); // Set slash policy (attestation-based: requiresProof=true, appealWindow=0) slashing_mgr @@ -715,8 +888,23 @@ async fn test_onchain_valid_attestation_executes_slash() { .await .unwrap(); - // Set committee: 3 voters, threshold M=2 + let stored_policy = slashing_mgr + .getSlashPolicy(reason) + .call() + .await + .expect("getSlashPolicy should succeed"); + assert!( + stored_policy.enabled, + "slash policy must be enabled after setSlashPolicy" + ); + assert!( + stored_policy.requiresProof, + "slash policy must be attestation-based (requiresProof)" + ); + + // Set committee: operator + 3 voters, threshold M=2 (operator must be a member) let committee = vec![ + operator_addr, voter_signer1.address(), voter_signer2.address(), voter_signer3.address(), @@ -738,42 +926,43 @@ async fn test_onchain_valid_attestation_executes_slash() { .await .unwrap(); - // All 3 voters sign accusation votes (agrees=true) + // All 3 voters sign accusation votes let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); let data_hash = FixedBytes::from([0xaa; 32]); let (v1, s1) = sign_vote( &voter_signer1, chain_id, + sm_addr, e3_id, accusation_id, - true, data_hash, ); let (v2, s2) = sign_vote( &voter_signer2, chain_id, + sm_addr, e3_id, accusation_id, - true, data_hash, ); let (v3, s3) = sign_vote( &voter_signer3, chain_id, + sm_addr, e3_id, accusation_id, - true, data_hash, ); let evidence = encode_attestation_evidence( proof_type, vec![ - (v1, true, data_hash, s1), - (v2, true, data_hash, s2), - (v3, true, data_hash, s3), + (v1, data_hash, s1), + (v2, data_hash, s2), + (v3, data_hash, s3), ], + VOTE_NO_EXPIRY, ); // Verify proposal count before @@ -790,7 +979,7 @@ async fn test_onchain_valid_attestation_executes_slash() { // Submit slash — should succeed (3 valid votes, threshold M=2) let receipt = slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence) + .proposeSlash(U256::from(e3_id), operator_addr, evidence) .send() .await .expect("proposeSlash tx should not fail to send") @@ -853,9 +1042,9 @@ async fn test_onchain_insufficient_attestations_reverts() { let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; let slashing_mgr = SlashingManager::new(sm_addr, &provider); - let reason: FixedBytes<32> = keccak256("E3_BAD_DKG_PROOF"); let e3_id: u64 = 42; let proof_type = 0u8; + let reason = reason_for_proof_type(proof_type); slashing_mgr .setSlashPolicy( @@ -879,11 +1068,12 @@ async fn test_onchain_insufficient_attestations_reverts() { .await .unwrap(); - // Committee: 3 voters, threshold M=2 + // Committee: operator + 3 voters, threshold M=2 mock_registry .setCommitteeNodes( U256::from(e3_id), vec![ + operator_addr, voter_signer1.address(), voter_signer2.address(), voter_signer3.address(), @@ -911,16 +1101,17 @@ async fn test_onchain_insufficient_attestations_reverts() { let (v1, s1) = sign_vote( &voter_signer1, chain_id, + sm_addr, e3_id, accusation_id, - true, data_hash, ); - let evidence = encode_attestation_evidence(proof_type, vec![(v1, true, data_hash, s1)]); + let evidence = + encode_attestation_evidence(proof_type, vec![(v1, data_hash, s1)], VOTE_NO_EXPIRY); let result = slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence) + .proposeSlash(U256::from(e3_id), operator_addr, evidence) .call() .await; @@ -931,7 +1122,7 @@ async fn test_onchain_insufficient_attestations_reverts() { let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("InsufficientAttestations"), + err_has_selector(&err_string, SEL_INSUFFICIENT_ATTESTATIONS), "expected InsufficientAttestations revert, got: {err_string}" ); @@ -970,9 +1161,9 @@ async fn test_onchain_voter_not_in_committee_reverts() { let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; let slashing_mgr = SlashingManager::new(sm_addr, &provider); - let reason: FixedBytes<32> = keccak256("E3_BAD_DKG_PROOF"); let e3_id: u64 = 42; let proof_type = 0u8; + let reason = reason_for_proof_type(proof_type); slashing_mgr .setSlashPolicy( @@ -996,9 +1187,12 @@ async fn test_onchain_voter_not_in_committee_reverts() { .await .unwrap(); - // Committee only contains committee_signer, NOT outsider_signer + // Committee: operator + committee_signer (outsider is NOT a member) mock_registry - .setCommitteeNodes(U256::from(e3_id), vec![committee_signer.address()]) + .setCommitteeNodes( + U256::from(e3_id), + vec![operator_addr, committee_signer.address()], + ) .send() .await .unwrap() @@ -1021,16 +1215,17 @@ async fn test_onchain_voter_not_in_committee_reverts() { let (v_out, s_out) = sign_vote( &outsider_signer, chain_id, + sm_addr, e3_id, accusation_id, - true, data_hash, ); - let evidence = encode_attestation_evidence(proof_type, vec![(v_out, true, data_hash, s_out)]); + let evidence = + encode_attestation_evidence(proof_type, vec![(v_out, data_hash, s_out)], VOTE_NO_EXPIRY); let result = slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence) + .proposeSlash(U256::from(e3_id), operator_addr, evidence) .call() .await; @@ -1041,7 +1236,7 @@ async fn test_onchain_voter_not_in_committee_reverts() { let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("VoterNotInCommittee"), + err_has_selector(&err_string, SEL_VOTER_NOT_IN_COMMITTEE), "expected VoterNotInCommittee revert, got: {err_string}" ); @@ -1080,9 +1275,9 @@ async fn test_onchain_invalid_vote_signature_reverts() { let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; let slashing_mgr = SlashingManager::new(sm_addr, &provider); - let reason: FixedBytes<32> = keccak256("E3_BAD_DKG_PROOF"); let e3_id: u64 = 42; let proof_type = 0u8; + let reason = reason_for_proof_type(proof_type); slashing_mgr .setSlashPolicy( @@ -1106,9 +1301,12 @@ async fn test_onchain_invalid_vote_signature_reverts() { .await .unwrap(); - // victim_signer is in the committee + // operator + victim_signer are committee members mock_registry - .setCommitteeNodes(U256::from(e3_id), vec![victim_signer.address()]) + .setCommitteeNodes( + U256::from(e3_id), + vec![operator_addr, victim_signer.address()], + ) .send() .await .unwrap() @@ -1131,30 +1329,30 @@ async fn test_onchain_invalid_vote_signature_reverts() { // Sign using impersonator's key but construct the digest for victim_signer's address let digest = compute_vote_digest( chain_id, + sm_addr, e3_id, accusation_id, victim_signer.address(), - true, data_hash, + VOTE_NO_EXPIRY, ); let bad_sig = impersonator_signer - .sign_message_sync(digest.as_ref()) + .sign_hash_sync(&digest) .expect("signing should succeed"); // Build evidence claiming the vote is from victim_signer but signed by impersonator - let evidence = Bytes::from( - ( - U256::from(proof_type), - vec![victim_signer.address()], - vec![true], - vec![data_hash], - vec![Bytes::from(bad_sig.as_bytes().to_vec())], - ) - .abi_encode(), - ); + let evidence: Bytes = ( + U256::from(proof_type), + vec![victim_signer.address()], + vec![data_hash], + VOTE_NO_EXPIRY, + vec![Bytes::from(bad_sig.as_bytes().to_vec())], + ) + .abi_encode_params() + .into(); let result = slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence) + .proposeSlash(U256::from(e3_id), operator_addr, evidence) .call() .await; @@ -1165,7 +1363,7 @@ async fn test_onchain_invalid_vote_signature_reverts() { let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("InvalidVoteSignature"), + err_has_selector(&err_string, SEL_INVALID_VOTE_SIGNATURE), "expected InvalidVoteSignature revert, got: {err_string}" ); @@ -1206,9 +1404,9 @@ async fn test_onchain_duplicate_voter_reverts() { let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; let slashing_mgr = SlashingManager::new(sm_addr, &provider); - let reason: FixedBytes<32> = keccak256("E3_BAD_DKG_PROOF"); let e3_id: u64 = 42; let proof_type = 0u8; + let reason = reason_for_proof_type(proof_type); slashing_mgr .setSlashPolicy( @@ -1233,7 +1431,10 @@ async fn test_onchain_duplicate_voter_reverts() { .unwrap(); mock_registry - .setCommitteeNodes(U256::from(e3_id), vec![voter_signer.address()]) + .setCommitteeNodes( + U256::from(e3_id), + vec![operator_addr, voter_signer.address()], + ) .send() .await .unwrap() @@ -1256,27 +1457,26 @@ async fn test_onchain_duplicate_voter_reverts() { let (voter, sig) = sign_vote( &voter_signer, chain_id, + sm_addr, e3_id, accusation_id, - true, data_hash, ); // Submit evidence with duplicate voter entries (bypassing encode_attestation_evidence // which would deduplicate — construct manually to have same address appear twice) - let evidence = Bytes::from( - ( - U256::from(proof_type), - vec![voter, voter], // duplicate! - vec![true, true], - vec![data_hash, data_hash], - vec![sig.clone(), sig], - ) - .abi_encode(), - ); + let evidence: Bytes = ( + U256::from(proof_type), + vec![voter, voter], + vec![data_hash, data_hash], + VOTE_NO_EXPIRY, + vec![sig.clone(), sig], + ) + .abi_encode_params() + .into(); let result = slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence) + .proposeSlash(U256::from(e3_id), operator_addr, evidence) .call() .await; @@ -1287,7 +1487,7 @@ async fn test_onchain_duplicate_voter_reverts() { let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("DuplicateVoter"), + err_has_selector(&err_string, SEL_DUPLICATE_VOTER), "expected DuplicateVoter revert, got: {err_string}" ); @@ -1326,9 +1526,9 @@ async fn test_onchain_duplicate_evidence_reverts() { let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; let slashing_mgr = SlashingManager::new(sm_addr, &provider); - let reason: FixedBytes<32> = keccak256("E3_BAD_DKG_PROOF"); let e3_id: u64 = 42; let proof_type = 0u8; + let reason = reason_for_proof_type(proof_type); slashing_mgr .setSlashPolicy( @@ -1355,7 +1555,11 @@ async fn test_onchain_duplicate_evidence_reverts() { mock_registry .setCommitteeNodes( U256::from(e3_id), - vec![voter_signer1.address(), voter_signer2.address()], + vec![ + operator_addr, + voter_signer1.address(), + voter_signer2.address(), + ], ) .send() .await @@ -1378,28 +1582,29 @@ async fn test_onchain_duplicate_evidence_reverts() { let (v1, s1) = sign_vote( &voter_signer1, chain_id, + sm_addr, e3_id, accusation_id, - true, data_hash, ); let (v2, s2) = sign_vote( &voter_signer2, chain_id, + sm_addr, e3_id, accusation_id, - true, data_hash, ); let evidence = encode_attestation_evidence( proof_type, - vec![(v1, true, data_hash, s1), (v2, true, data_hash, s2)], + vec![(v1, data_hash, s1), (v2, data_hash, s2)], + VOTE_NO_EXPIRY, ); // First submission should succeed slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence.clone()) + .proposeSlash(U256::from(e3_id), operator_addr, evidence.clone()) .send() .await .expect("first proposeSlash should succeed") @@ -1409,7 +1614,7 @@ async fn test_onchain_duplicate_evidence_reverts() { // Second submission with same evidence should revert let result = slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence) + .proposeSlash(U256::from(e3_id), operator_addr, evidence) .call() .await; @@ -1420,9 +1625,186 @@ async fn test_onchain_duplicate_evidence_reverts() { let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("DuplicateEvidence"), + err_has_selector(&err_string, SEL_DUPLICATE_EVIDENCE), "expected DuplicateEvidence revert, got: {err_string}" ); println!("PASS: duplicate evidence correctly reverts — replay protection verified"); } + +// ════════════════════════════════════════════════════════════════════════════ +// End-to-end actor parity (Anvil) +// +// Drives the production `AccusationManager::vote_digest` and +// `e3_evm::encode_attestation_evidence` against a deployed `SlashingManager` +// on Anvil. Catches drift between off-chain signing/encoding and the +// on-chain decoder/recover that hand-rolled reference helpers cannot — if +// any of (typehash string, domain literal, field order, deadline binding) +// silently diverges, this test reverts on-chain. +// ════════════════════════════════════════════════════════════════════════════ + +/// The actor's `AccusationManager::vote_digest` + `e3_evm::encode_attestation_evidence` +/// must produce calldata that `SlashingManager._verifyAttestationEvidence` +/// accepts. This is the canonical "actor → Solidity" end-to-end test. +#[tokio::test] +async fn test_onchain_actor_signed_vote_accepted() { + use e3_events::{AccusationOutcome, AccusationQuorumReached, AccusationVote, ProofType}; + use e3_evm::encode_attestation_evidence; + use e3_zk_prover::AccusationManager; + + if !find_anvil().await { + println!("skipping: anvil not found on PATH"); + return; + } + + let (sm_bytecode, mr_bytecode) = match load_slashing_artifacts() { + Some(artifacts) => artifacts, + None => { + println!( + "skipping: contract artifacts not found \ + (run `npx hardhat compile` in packages/enclave-contracts)" + ); + return; + } + }; + + let provider = ProviderBuilder::new().connect_anvil_with_wallet(); + let chain_id = provider.get_chain_id().await.unwrap(); + + let voter1 = PrivateKeySigner::random(); + let voter2 = PrivateKeySigner::random(); + let voter3 = PrivateKeySigner::random(); + let operator_addr: Address = "0x4444444444444444444444444444444444444444" + .parse() + .unwrap(); + + let mock_registry_addr = deploy_contract(&provider, &mr_bytecode, &[]).await; + let mock_registry = MockCiphernodeRegistry::new(mock_registry_addr, &provider); + let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; + let slashing_mgr = SlashingManager::new(sm_addr, &provider); + + let e3_id: u64 = 7; + let proof_type = 0u8; + let reason = reason_for_proof_type(proof_type); + + // Enable an attestation-based policy. `appealWindow = 0` keeps the + // assertion focused on the verifier path (the slash auto-executes). + slashing_mgr + .setSlashPolicy( + reason, + SlashingManager::SlashPolicy { + ticketPenalty: U256::from(50_000_000u64), + licensePenalty: U256::from(100_000_000_000_000_000_000u128), + requiresProof: true, + proofVerifier: Address::ZERO, + banNode: false, + appealWindow: U256::ZERO, + enabled: true, + affectsCommittee: false, + failureReason: 0u8, + }, + ) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + // Committee = operator + 3 voters; threshold M=2. + let committee = vec![ + operator_addr, + voter1.address(), + voter2.address(), + voter3.address(), + ]; + mock_registry + .setCommitteeNodes(U256::from(e3_id), committee) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + mock_registry + .setThreshold(U256::from(e3_id), 2u32) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + let data_hash = FixedBytes::from([0xee; 32]); + + // Pick a deadline far in the future so the on-chain check passes + // regardless of Anvil's block.timestamp at submission time. + let deadline: u64 = u64::MAX / 2; + + let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); + + // Build & sign three votes via the **production** code path: + // 1. Construct `AccusationVote` exactly as the actor would. + // 2. Compute the digest via the actor's `vote_digest`. + // 3. Sign with `signer.sign_hash_sync` (same as `sign_vote_digest`). + let make_actor_vote = |signer: &PrivateKeySigner| -> AccusationVote { + let voter = signer.address(); + let mut vote = AccusationVote { + e3_id: e3_events::E3id::new(e3_id.to_string(), chain_id), + accusation_id: *accusation_id.as_ref(), + voter, + data_hash: *data_hash.as_ref(), + deadline, + signature: ArcBytes::default(), + }; + let digest = AccusationManager::vote_digest(&vote, sm_addr); + let sig = signer + .sign_hash_sync(&FixedBytes::<32>::from(digest)) + .expect("vote sign"); + vote.signature = ArcBytes::from_bytes(&sig.as_bytes()); + vote + }; + + let votes_for = vec![ + make_actor_vote(&voter1), + make_actor_vote(&voter2), + make_actor_vote(&voter3), + ]; + + // Build the event the production writer consumes and encode via the + // **production** encoder. If either side has drifted from Solidity, + // `proposeSlash` will revert (InvalidVoteSignature, EquivocationDetected, + // or ABI-decode failure). + let quorum = AccusationQuorumReached { + e3_id: e3_events::E3id::new(e3_id.to_string(), chain_id), + accuser: voter1.address(), + accused: operator_addr, + proof_type: ProofType::C0PkBfv, + votes_for, + outcome: AccusationOutcome::AccusedFaulted, + evidence: Bytes::new(), // audit metadata only; not on chain + }; + let evidence = encode_attestation_evidence(&quorum) + .expect("encode_attestation_evidence must produce bytes for nonempty votes_for"); + + // Submit. If anything in the actor → writer → Solidity chain disagrees, + // this call reverts. The decoded Solidity error is far more informative + // than a digest-mismatch assertion, which is the whole point of running + // against the real contract. + let receipt = slashing_mgr + .proposeSlash(U256::from(e3_id), operator_addr, Bytes::from(evidence)) + .send() + .await + .expect("actor-signed proposeSlash must succeed (off-chain ↔ on-chain digest parity)") + .get_receipt() + .await + .expect("proposeSlash receipt obtainable"); + assert!( + receipt.status(), + "actor-signed proposeSlash must land on chain — receipt status was false" + ); + println!( + "PASS: actor-signed AccusationVote accepted by Solidity verifier (tx={:?})", + receipt.transaction_hash + ); +} diff --git a/docs/pages/cryptography.mdx b/docs/pages/cryptography.mdx index 1740456a14..abc0baf0f4 100644 --- a/docs/pages/cryptography.mdx +++ b/docs/pages/cryptography.mdx @@ -196,9 +196,6 @@ keep different commitment kinds from being confused with one another. ### Proof aggregation -> **Note:** this section describes a proposed design that has not been implemented yet. It is -> included here as the target architecture for proof aggregation and on-chain verification. - Individual circuit proofs cover each step in isolation, but the protocol must land them on-chain as a compact, verifiable package. A naive fold of all proofs into one recursive aggregate produces an opaque commitment that the contract cannot interpret without the original public inputs, and if @@ -209,20 +206,98 @@ expected commitments match outputs) is only checked off-chain, end-to-end correctness still depends on trusting the committee network rather than on verifiable mathematics. -The aggregation design addresses both problems. Only **two recursive proofs** are posted on-chain -per E3 session: +The aggregation design addresses both problems. When an E3 is requested with +`proofAggregationEnabled = true`, only **two recursive proofs** are posted on-chain per E3 session: -1. **DKG recursive proof**: the aggregator recursively verifies every node's C0-C4 fold chain - together with **C5**, which aggregates the public key. +1. **DKG recursive proof**: the aggregator recursively verifies every node's C0–C4 fold chain + together with **C5**, which aggregates the public key. Verified on-chain by + [`BfvPkVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol) + at `publishCommittee`. 2. **Decryption recursive proof**: the aggregator recursively verifies **C6** proofs from at least - **T+1** parties together with **C7**, which reconstructs the plaintext. - -Nodes do not post proofs on-chain. Each node folds its own C0-C4 chain, **signs** the fold output, -and relays it to the aggregator via P2P gossip. The recursive proof verifies each signature inside -ZK, so the aggregator cannot substitute or fabricate any node's contribution without the proof -failing. During registration the contract stores the submitting address for each `party_id`; -verification recovers the signer via `ecrecover` and checks it matches, binding each fold output to -its creator. + **T+1** parties together with **C7**, which reconstructs the plaintext. Verified on-chain by + [`BfvDecryptionVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol) + at `publishPlaintextOutput`. + +Individual nodes do not post their inner proofs on-chain. Each node folds its own C0–C4 chain into a +`NodeDkgFold` proof, **signs** the surfaced commitments, and relays the signed fold output to the +aggregator via P2P gossip. The aggregator then assembles the recursive DKG proof from all honest +nodes' fold outputs and submits it together with a **fold-attestation bundle**. + +#### Per-node attestations and topNodes binding + +The fold-attestation bundle is the on-chain link from "this aggregated proof was produced by the +right operators" to "this specific signature came from the registered operator for `partyId = i`". +At `publishCommittee` the registry has already finalised `topNodes` (the address-sorted committee; +party 0 = lowest address, party 1 = next, ...), so the on-chain canonical mapping from a `partyId` +to an operator address is `topNodes[partyId]`. + +Each node's fold attestation signs the canonical **EIP-712 typed-data digest** +(`keccak256("\x19\x01" || domainSeparator || structHash)`). The **domain separator** binds `chainId` +and `verifyingContract` (the deployed `DkgFoldAttestationVerifier` address); off-chain signers must +include those domain fields. The **struct hash** binds `(e3Id, partyId, skAggCommit, esmAggCommit)` +— the tuple values in the signed payload, separate from the domain. The on-chain +[`DkgFoldAttestationVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol) +then enforces four properties per attestation: + +1. **Slot binding**: `canonicalCommitteeNodeAt(e3Id, binding.partyId) == binding.node`. The + aggregator cannot claim that the operator at `topNodes[0]` is party 1 — the registry's canonical + slot mapping is checked directly. Available before `publishCommittee` completes (the registry + sorts `topNodes` at finalisation, well before the attestation verifier runs). +2. **Signer = registered operator**: `ecrecover` on the EIP-712 digest (with this verifier as + `verifyingContract`) must equal `binding.node`, and the address must be an active committee + member for this E3 (`isCommitteeMemberActive(e3Id, node)`). +3. **Commitments match the DKG proof**: the signed `skAggCommit` and `esmAggCommit` must equal the + `partyId`'s slot in the DKG aggregator proof's public inputs. +4. **Honest-set cardinality**: the bundle must contain exactly one attestation per honest party in + the proof, with `partyId` strictly ascending (no duplicates, no partial subsets). + +This closes the per-operator attribution gap: the aggregator cannot present a valid DKG proof +without also producing one signed attestation per honest party, each bound to the operator +registered at the corresponding `topNodes` slot, and slot-shuffling between operators is +structurally prevented. The recovered `partyIds`, `skAggCommits`, and `esmAggCommits` are persisted +as on-chain anchors (`dkgPartyIds`, `dkgSkAggCommits`, `dkgEsmAggCommits`) so future decryption +proofs can be checked against them. + +#### Per-operator attribution for slashing + +When a node misbehaves, the off-chain accusation quorum protocol produces a +[`SlashingManager.proposeSlash`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/slashing/SlashingManager.sol) +call. The on-chain check enforces, in `_verifyAttestationEvidence`: + +1. **Voter quorum and signatures**: ≥ `M` committee-member voters (sorted ascending, accused + excluded), each signing the canonical EIP-712 typed-data hash over + `AccusationVote(uint256 e3Id, bytes32 accusationId, address voter, bytes32 dataHash, uint256 deadline)`. + `chainId` and `verifyingContract = address(SlashingManager)` live in the EIP-712 domain, not the + struct — so signatures can't be replayed across chains or against a different contract. +2. **Same evidence across voters**: all `dataHashes[i]` must equal a common `dataHash`. Without this + each voter could be agreeing to "something bad" of their own — the quorum would not actually + agree on what they observed. +3. **Shared signature deadline**: every voter signs the same `deadline` (chosen by the accuser), and + the contract enforces `block.timestamp <= deadline`. Stolen signatures can't be replayed + indefinitely; the validity window is governance-mutable via the registry's + `accusationVoteValidity`. +4. **Agreement is implicit**: the gossip wire no longer carries disagreement — a peer who finds the + disputed proof passes simply stays silent. Every signature submitted in evidence is an agreement, + so an `agrees` flag is no longer signed or transported. + +The faulty-proof bytes themselves are kept as off-chain audit metadata on the +`AccusationQuorumReached` event so any observer can independently re-derive each voter's `dataHash`; +they are intentionally not on chain (the `dataHash` equality check is all the contract needs for +attribution, and dropping the preimage saves significant calldata). + +Combined with the fold-attestation chain, the slashing flow still forms a complete attribution +chain: operator → registered address at `topNodes[partyId]` → EIP-712 signature on a specific +`(e3Id, accusationId, dataHash, deadline)` tuple → off-chain audit bytes recovering the original +proof. Both the DKG fault-attribution side and the slashing-evidence side are cryptographically +bound on chain. + +#### Committee hash binding + +The DKG proof's public inputs include a `committee_hash` split into 128-bit hi/lo limbs. +`CommitteeHashLib.hash` computes this as `keccak256` over the raw 20-byte addresses of `topNodes` +concatenated in address-ascending order (the same canonical order the off-chain Rust side uses, so +the hashes match byte-for-byte). This binds the proof to the exact committee finalised on-chain — no +other permutation of those addresses, and no other set of addresses, would produce a matching hash. #### Surfaced anchor values @@ -443,7 +518,12 @@ 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. +see [Noir Circuits](./noir-circuits) for how to build and verify them. The on-chain Honk verifier +contracts that consume the outer `dkg_aggregator` and `decryption_aggregator` proofs are generated +from the circuits' recursive VKs and committed to git; the repo enforces they stay in lockstep via +`pnpm generate:verifiers --check` (run automatically by integration tests and benchmarks). See +[`internals/dkg#keeping-the-on-chain-honk-verifiers-in-sync`](./internals/dkg#keeping-the-on-chain-honk-verifiers-in-sync) +for the contract. ## Where to go next diff --git a/docs/pages/internals/dkg.mdx b/docs/pages/internals/dkg.mdx index 128629f360..28b217ac1a 100644 --- a/docs/pages/internals/dkg.mdx +++ b/docs/pages/internals/dkg.mdx @@ -258,7 +258,7 @@ ciphernode in **A**. ## P2: Public Key Aggregation (C5) -The aggregator (party_id=0) collects all public key shares from C1 and sums them: +The aggregator (the elected node for this E3) collects all public key shares from C1 and sums them: ``` pk_threshold = Σ pk_share_i for i ∈ A @@ -328,6 +328,261 @@ aggregator. --- +## Proof Aggregation and On-Chain Anchoring + +E3 programs can opt into **proof aggregation** by setting `proofAggregationEnabled = true` at +request time. In that mode the network does not post per-node, per-circuit proofs on-chain. Instead +the aggregator submits two recursive proofs — one for DKG, one for decryption — together with +per-node attestations that bind each surfaced commitment to a specific registered operator. + +### Canonical party_id assignment + +`party_id` is each node's index in the committee's canonical ordering. On chain, the committee is +finalised as `topNodes`, sorted by **ascending address** (lowest numeric address = party 0). The +ciphernodes derive their own `party_id` from `position()` in the `CommitteeFinalized.committee` +array, which carries the same canonical order. Address-sort is deterministic and reproducible by +both sides from just the set of committee addresses, so the Rust and Solidity sides never need to +agree on per-run sortition scores to agree on the labeling. + +### Committee hash binding + +Both recursive proofs include a `committee_hash` in their public inputs, split into 128-bit +`committee_hash_hi` and `committee_hash_lo` for the BN254 field. The hash is `keccak256` over the +raw 20-byte addresses of `topNodes` concatenated in address-ascending order (see +`CommitteeHashLib.hash` in the registry and `e3_utils::committee_hash::hash_committee_addresses` in +Rust). The two implementations produce byte-identical output for the same committee, so the proof's +bound hash matches whatever the registry computed at `publishCommittee` — failing the verifier +otherwise. + +### NodeDkgFold and fold attestations + +After completing C0–C4 locally, each ciphernode folds its proofs into a single `NodeDkgFold` proof +that surfaces three values: `sk_agg_commit`, `esm_agg_commit`, and the node's `party_id`. The node +then signs an EIP-712-style digest over `(chainId, e3Id, partyId, skAggCommit, esmAggCommit)` with +its operator key and gossips the signed fold output to peers via `DKGRecursiveAggregationComplete`. + +In proof-aggregation mode, the aggregator collects all honest parties' fold outputs and: + +1. Recursively verifies each `NodeDkgFold` inside the DKG aggregator proof (so an aggregator cannot + fabricate a node's fold without the recursive proof failing). +2. Assembles a **DKG fold-attestation bundle**: one signed attestation per honest party plus a + `(partyId → node address)` binding table. + +The DKG aggregator proof surfaces, as public outputs, the array of `party_ids`, `sk_agg_commits[]`, +`esm_agg_commits[]`, the committee hash limbs, and the aggregated public-key commitment. + +### `publishCommittee` verification + +When the aggregator calls +[`CiphernodeRegistry.publishCommittee`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol), +the registry runs two checks in order: + +1. **Honk proof verification** via + [`BfvPkVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol). + It checks the recursive VK hashes for `node_fold` and `pk_aggregation` (so the underlying + circuits are the deployed ones), the committee-hash hi/lo limbs match + `CommitteeHashLib.hash(c.topNodes)`, the surfaced `pkCommitment` equals the caller-supplied + value, and the raw Honk proof verifies. +2. **Fold-attestation verification** via + [`DkgFoldAttestationVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol). + It walks the bundle's `(partyId, node)` bindings — strictly ascending by `partyId`, no duplicates + — and for each binding: requires `canonicalCommitteeNodeAt(e3Id, partyId) == node` (slot + binding), requires `isCommitteeMemberActive(e3Id, node)`, recovers the signer via `ecrecover` on + the canonical EIP-712 typed-data digest (with `chainId` and this verifier address in the domain + separator), requires `signer == node`, and checks that the signed `skAggCommit` / `esmAggCommit` + match the DKG proof's surfaced commitments at that party's slot. The honest-set cardinality must + equal the honest-party count derived from the proof's public-input shape (`(len - 6) / 3`). The + slot binding prevents an aggregator from reassigning a node's signed attestation to a different + slot — even an actively cooperating signer cannot be "moved" between partyIds because the + registry's own slot mapping is checked structurally. + +Only if both succeed does the registry store the per-party anchors: + +```solidity +dkgPartyIds[e3Id] = partyIds; +dkgSkAggCommits[e3Id] = skAgg; +dkgEsmAggCommits[e3Id] = esmAgg; +``` + +These anchors are what downstream decryption verifies against, and what slashing-by-attribution can +read to identify which registered operator produced which DKG output. The mapping +`operator = topNodes[partyIds[i]]` is a closed cryptographic chain: the proof binds to the addresses +(via committee hash), the attestation binds to the proof commitments and the operator key, and the +operator key was registered against `topNodes` at committee finalisation. + +### Registry-backed runtime config (operator restart requirement) + +Two registry storage values are fetched **once at process startup** and reused for the entire +process lifetime: + +1. `dkgFoldAttestationVerifier` — the EIP-712 `verifyingContract` for fold attestations (see + [`fetch_dkg_fold_attestation_verifier`](https://github.com/gnosisguild/enclave/blob/main/crates/evm/src/ciphernode_registry_sol.rs)). +2. `accusationVoteValidity` — the seconds-window applied to accusation vote `deadline`s (see the + [Registry-wide vote validity window](#registry-wide-vote-validity-window) subsection). + +Both follow the same restart-required lifecycle: governance updates take effect for new processes +only, and live nodes keep using the value they fetched at startup. + +#### Verifier rotation + +The address of the on-chain `DkgFoldAttestationVerifier` is governance-mutable on the registry via a +2-day timelocked propose / commit / cancel flow (`DKG_FOLD_VERIFIER_TIMELOCK`). This is intentional +— the verifier is treated as a critical admin key so a compromised owner cannot instantly swap in a +weakened verifier that bypasses per-party attestation checks. + +**Node-operator requirement.** After a successful `commitDkgFoldAttestationVerifier`, signatures +produced by long-running nodes will be rejected on-chain by the new verifier — different +`verifyingContract` means a different EIP-712 domain separator, so `ECDSA.recover` returns the wrong +address, the canonical-slot binding check fails, and aggregators will treat those nodes as dishonest +until they restart. + +The 2-day timelock window exists for exactly this reason: it gives operators time to coordinate a +rolling restart before the swap becomes effective. Operators MUST restart all ciphernodes within +`DKG_FOLD_VERIFIER_TIMELOCK` of the propose event being emitted; this is a strict requirement, not a +recommendation. Nodes that miss the window silently produce invalid attestations until restarted. +The same rule applies to the one-shot deploy-time `setInitialDkgFoldAttestationVerifier`, except +that call is only valid when no verifier is yet configured. + +#### Vote validity changes + +`setAccusationVoteValidity` is not timelocked — governance can change the window instantly. The +restart requirement is therefore weaker than for the verifier (an updated window doesn't reject old +signatures, it only shortens or extends the freshness check for future ones), but the same "live +nodes keep stamping deadlines from the old value" caveat applies. After a non-trivial shortening, +coordinate a rolling restart so every node observes the new window before any votes are signed +against it. See the [Registry-wide vote validity window](#registry-wide-vote-validity-window) +subsection for the emergency-stop semantics (setting it to 0). + +**Multi-chain caveat.** When `enclave.config.yaml` lists more than one chain, the node reads +`dkgFoldAttestationVerifier` and `accusationVoteValidity` from the **first enabled** chain only +(`fetch_dkg_fold_attestation_verifier_from_registry` / +`fetch_accusation_vote_validity_from_registry` in `ciphernode-builder`). That single value is then +used for every E3 the node participates in, regardless of which chain the E3 lives on. This is a +deliberate trade-off: + +- **Today most operators run single-chain nodes**, so the practical risk is zero. +- A multi-chain node whose first-enabled chain has a _stricter_ setter for either knob produces + values the other chain's contract may refuse — e.g. a `verifyingContract` on chain A is + meaningless when chain B's verifier recomputes the EIP-712 domain against its own address. There + is no in-flight fallback. +- If you add a new chain to a running node's config, or rotate any chain's verifier / vote-validity + window, restart the node so all per-chain fetches re-run. + +A robust per-E3 lookup (re-fetch from the chain that owns the E3 every time we sign or encode) is +left as future work — the design is straightforward but the wiring crosses multiple actors and isn't +worth the surface area until a multi-chain deployment makes the trade-off concrete. + +### Keeping the on-chain Honk verifiers in sync + +The two on-chain Honk verifiers consumed by `publishCommittee` and the decryption flow +(`DkgAggregatorVerifier.sol`, `DecryptionAggregatorVerifier.sol`, both under +`packages/enclave-contracts/contracts/verifiers/bfv/honk/`) are **generated** from the recursive VKs +of `recursive_aggregation/dkg_aggregator` and `recursive_aggregation/decryption_aggregator` via +`bb write_solidity_verifier`. They are committed to git so production deploys are reproducible, but +they must stay in lockstep with the circuits' recursive VKs — a verifier whose baked-in VK doesn't +match the circuit will silently reject every proof. + +**Preset is pinned.** The committed `.sol` files correspond to exactly one BFV preset: +**`insecure-512`** (the dev / CI / benchmark default). The recursive VKs are preset-dependent — +different BFV parameter sets compile to different VKs and therefore different `.sol` bytes — so "the +committed verifier" is only well-defined for one preset. `pnpm generate:verifiers` refuses to run +(in both `--check` and `--write` modes) unless `dist/circuits/insecure-512/.build-stamp.json` exists +and reports that preset. If `pnpm build:circuits --preset secure-8192` was the last build, the +generator surfaces this loudly with a fix recipe instead of silently producing wrong VK bytes. +Production deploys on a non-canonical preset must regenerate locally for that deploy. + +The repo enforces sync with two modes of `pnpm generate:verifiers`: + +- **`--check`** — used by `tests/integration/lib/prebuild.sh` (when + `PROOF_AGGREGATION_ENABLED=true`) and by the benchmark gas-extraction scripts. Regenerates each + verifier in memory, runs the same `prettier-plugin-solidity` formatting the committed files use, + and diffs against the committed `.sol`. Any drift fails the run with a clear fix recipe instead of + silently rewriting committed contracts mid-test. +- **`--write`** (the default for manual invocation) — overwrites the committed files. Run this when + you intentionally bump the canonical-preset circuits or the Noir/bb toolchain, then commit the + diff. + +The version pinning lives in `crates/zk-prover/versions.json` (consumed by `enclave noir setup` and +by the integration test prebuild). If `--check` fails after a clean +`pnpm build:circuits --preset insecure-512`, your local toolchain is out of sync with that pin. See +[`scripts/README.md`](https://github.com/gnosisguild/enclave/blob/main/scripts/README.md#verifier-generator) +for the full contract. + +### Decryption recursive proof + +The decryption recursive proof (`BfvDecryptionVerifier`) follows the same shape: a recursive Honk +proof aggregates **C6** from at least **T+1** parties plus **C7**, surfaces `expected_sk_commits[]` +and `expected_esm_commits[]` per party, and binds to the on-chain committee hash. The registry +compares the surfaced commitments against the anchors stored at `publishCommittee` — if any anchor +differs the proof is rejected — and the plaintext is decoded from the proof's last 100 public inputs +(one `uint64` coefficient each, packed into bytes). + +### Slashing-evidence binding + +When fault attribution leads to a slash, the off-chain accusation quorum produces a payload that +[`SlashingManager.proposeSlash`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/slashing/SlashingManager.sol) +decodes as +`(uint256 proofType, address[] voters, bytes32[] dataHashes, bytes evidence, uint256 deadline, bytes[] signatures)`. +`_verifyAttestationEvidence` enforces: + +- ≥ `M` voters, sorted ascending, accused excluded, each producing a canonical EIP-712 typed-data + signature over + `AccusationVote(uint256 e3Id, bytes32 accusationId, address voter, bytes32 dataHash, uint256 deadline)`. + `chainId` and `verifyingContract = address(SlashingManager)` live in the EIP-712 domain — see + `EIP712_DOMAIN_NAME` ("EnclaveSlashing") and `EIP712_DOMAIN_VERSION` ("1") on the contract; the + Rust signer reads the exact same strings from `e3_events::VOTE_DOMAIN_NAME` / + `VOTE_DOMAIN_VERSION` / `VOTE_TYPEHASH_STR`. +- All `dataHashes[i]` equal a common `dataHash` (voters must agree on the same evidence, not just + "something bad"). +- `keccak256(evidence) == dataHash` (explicit preimage binding on chain). +- `block.timestamp <= deadline`. Every voter signs the same `deadline` value — the accuser stamps it + at proposal time as `now + accusationVoteValidity` (a registry-wide governance knob, see the + subsection below), and `AccusationManager::on_vote_received` rejects peers' votes whose deadline + disagrees so the aggregated evidence carries one shared value. Stolen signatures can't be replayed + indefinitely. +- Agreement is implicit: there is no `agrees` field on the signed struct or on the gossip wire. A + peer who finds the disputed proof passes simply stays silent; every signature reaching the + on-chain decoder is an affirmative attestation. See the + [`AccusationVote` docstring](https://github.com/gnosisguild/enclave/blob/main/crates/events/src/enclave_event/accusation_vote.rs) + for the rationale and the fast-fail trade-off this design accepts. + +The faulty-proof preimage (`abi.encode(proof.data, public_signals)`) is **not** transported on chain +— the previous `keccak256(evidence) == commonDataHash` check was dropped because the `dataHash` +equality across voters is sufficient for attribution and the calldata cost of carrying ~16 KB of +preimage per slash is not. The preimage is kept on the off-chain `AccusationQuorumReached` event as +audit metadata for observers; populated for every accusation path that leads to a slash (ZK +verification failures via `ProofVerificationFailed`, forwarded C3a/C3b accusations via the +re-verification path, and commitment-consistency violations via `CommitmentConsistencyViolation`). + +#### Registry-wide vote validity window + +The `accusationVoteValidity` storage on +[`CiphernodeRegistryOwnable`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol) +(default 30 minutes, seeded by `DEFAULT_ACCUSATION_VOTE_VALIDITY` at `initialize`) is the +seconds-window applied to every accusation vote's `deadline`. Ciphernodes fetch it once at process +startup via `fetch_accusation_vote_validity_from_registry` and reuse the value for the process +lifetime — same lifecycle as `dkgFoldAttestationVerifier`. + +Governance can change it with `setAccusationVoteValidity(uint256)`. Two notable knob positions: + +- **Shorter window**: tighter freshness — useful if stolen-signature replay becomes a concern, at + the cost of giving on-chain submitters less slack to land the `proposeSlash` transaction after + off-chain quorum reaches `M`. +- **Zero**: practically disables slashing — `accusationVoteValidity = 0` is a zero-second window, so + every new vote's `deadline` is stamped as `now` (no slack beyond the current block time). + Off-chain nodes still gossip votes; `proposeSlash` succeeds only if `SlashingManager` sees at + least **M** agreeing `AccusationVote` signatures and passes `block.timestamp <= deadline`. That + comparison is inclusive, so a vote whose `deadline` equals the current block time can still land + if `proposeSlash` is included in the **same block** — zero is an emergency brake, not a guaranteed + hard stop. Restore a non-zero window quickly to keep the slashing path operational. + +In-flight ciphernodes do not pick up the new value until restart. After a non-trivial shortening +(e.g. 30 min → 1 min) operators should coordinate a rolling restart, otherwise live nodes will keep +stamping deadlines from the old window and produce votes that the on-chain check rejects when the +round closes. + +--- + ## Proof Lifecycle ### Generation diff --git a/examples/CRISP/Readme.md b/examples/CRISP/Readme.md index 22731193a0..265dbf45c0 100644 --- a/examples/CRISP/Readme.md +++ b/examples/CRISP/Readme.md @@ -49,7 +49,11 @@ Before getting started, ensure you have installed: The simplest way to run CRISP is: ```bash -# Install dependencies and build everything +# Optional: choose local profile (copied to crisp.dev.env on first setup) +cp crisp.dev.env.example crisp.dev.env +# Edit CRISP_PROOF_AGGREGATION_ENABLED and CRISP_BFV_PRESET (see docs/PROOF_AGGREGATION_AND_ZK.md) + +# Install dependencies and build everything (applies crisp.dev.env → server/.env) pnpm dev:setup # Start all services (Hardhat, contracts, ciphernodes, program server, coordination server, and UI) @@ -169,7 +173,36 @@ program URL: The `pnpm dev:setup` command automatically creates `.env` files for the server and client from the `.env.example` templates (if they don't already exist). -The `enclave.config.yaml` file is automatically populated with contract addresses after deployment. +After `pnpm dev:up`, contract addresses are written automatically to `enclave.config.yaml`, +`server/.env`, and `client/.env` (no manual copy from `deployed_contracts.json`). + +### DKG proof aggregation and on-chain ZK + +Edit **`crisp.dev.env`** (created from `crisp.dev.env.example` on first `pnpm dev:setup`): + +| Variable | Default | Effect | +| --------------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------- | +| `CRISP_BFV_PRESET` | `insecure-512` | DKG circuit build preset when aggregation is on | +| `CRISP_PROOF_AGGREGATION_ENABLED` | `false` | Synced to `server/.env`; controls DKG circuit build, deploy (`ENABLE_ZK_VERIFICATION`), and runtime aggregation | + +`pnpm dev:setup` applies this profile (build DKG circuits when needed, sync `server/.env`). +`pnpm dev:up` deploys contracts using the same flags. + +See **[docs/PROOF_AGGREGATION_AND_ZK.md](./docs/PROOF_AGGREGATION_AND_ZK.md)** for modes, address +sync, and troubleshooting (`VkHashMismatch`, etc.). + +### Vercel (CRISP client) + +Deploy from **`examples/CRISP/client`**. The build uses the published **`@crisp-e3/sdk@0.9.0`** on +npm (`pnpm install --ignore-workspace`), not the monorepo workspace — so it does not compile Noir +circuits on Vercel. + +- **Project root directory:** `examples/CRISP/client` +- **`vercel build` in CI:** run from the **repository root** (not `cd examples/CRISP/client` first) +- Optional Vercel env: `ENABLE_EXPERIMENTAL_COREPACK=1` + +Commit `examples/CRISP/client/pnpm-lock.yaml` after dependency bumps +(`pnpm install --ignore-workspace` in that directory) for reproducible installs. ## Publishing packages to npm diff --git a/examples/CRISP/client/.env.example b/examples/CRISP/client/.env.example index 34cd2a418a..eee867acd2 100644 --- a/examples/CRISP/client/.env.example +++ b/examples/CRISP/client/.env.example @@ -1,7 +1,9 @@ VITE_ENCLAVE_API=http://127.0.0.1:4000 +VITE_CHAIN_ID=31337 +VITE_RPC_URL=http://127.0.0.1:8545 VITE_WALLETCONNECT_PROJECT_ID= -# A token with a public mint function. Also used as the fee token for Enclave. -# This is its default address in Anvil node -VITE_CRISP_TOKEN=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 +# Voting eligibility token (MockVotingToken). Updated automatically on `pnpm dev:up` deploy. +# Enclave fee token (MockUSDC) is separate: packages/enclave-contracts/deployed_contracts.json → MockUSDC. +VITE_CRISP_TOKEN=0x5081a39b8A5f0E35a8D959395a630b68B74Dd30f # Addresses of requesters for which to show rounds in the UI. Comma separated Ethereum addresses. VITE_E3_REQUESTERS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266,0x70997970C51812dc3A010C7d01b50e0d17dc79C8 diff --git a/examples/CRISP/client/.npmrc b/examples/CRISP/client/.npmrc new file mode 100644 index 0000000000..6906473d76 --- /dev/null +++ b/examples/CRISP/client/.npmrc @@ -0,0 +1,2 @@ +# Vercel / standalone installs: resolve @crisp-e3/sdk from npm (0.9.0), not the monorepo workspace. +ignore-workspace=true diff --git a/examples/CRISP/client/pnpm-lock.yaml b/examples/CRISP/client/pnpm-lock.yaml new file mode 100644 index 0000000000..c43765d010 --- /dev/null +++ b/examples/CRISP/client/pnpm-lock.yaml @@ -0,0 +1,11004 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@crisp-e3/sdk': + specifier: 0.9.0 + version: 0.9.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@emotion/babel-plugin': + specifier: ^11.11.0 + version: 11.13.5 + '@emotion/react': + specifier: ^11.11.4 + version: 11.14.0(@types/react@18.3.29)(react@18.3.1) + '@phosphor-icons/react': + specifier: ^2.1.4 + version: 2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@svgr/rollup': + specifier: ^8.1.0 + version: 8.1.0(rollup@4.60.4)(typescript@5.9.3) + '@tanstack/react-query': + specifier: ^5.74.3 + version: 5.100.14(react@18.3.1) + axios: + specifier: 1.13.2 + version: 1.13.2 + connectkit: + specifier: ^1.9.0 + version: 1.9.2(@babel/core@7.29.0)(@tanstack/react-query@5.100.14(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + ethers: + specifier: ^6.12.0 + version: 6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + react: + specifier: ^18.2.0 + version: 18.3.1 + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + react-markdown: + specifier: ^9.0.1 + version: 9.1.0(@types/react@18.3.29)(react@18.3.1) + react-router-dom: + specifier: ^6.22.3 + version: 6.30.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-syntax-highlighter: + specifier: ^15.5.0 + version: 15.6.6(react@18.3.1) + viem: + specifier: 2.38.6 + version: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + vite-plugin-node-polyfills: + specifier: ^0.22.0 + version: 0.22.0(rollup@4.60.4)(vite@5.4.21(@types/node@22.7.5)) + vite-plugin-top-level-await: + specifier: ^1.4.1 + version: 1.6.0(rollup@4.60.4)(vite@5.4.21(@types/node@22.7.5)) + vite-tsconfig-paths: + specifier: ^4.3.2 + version: 4.3.2(typescript@5.9.3)(vite@5.4.21(@types/node@22.7.5)) + wagmi: + specifier: ^2.14.16 + version: 2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + devDependencies: + '@tailwindcss/typography': + specifier: ^0.5.12 + version: 0.5.19(tailwindcss@3.4.19) + '@types/react': + specifier: ^18.2.66 + version: 18.3.29 + '@types/react-dom': + specifier: ^18.2.22 + version: 18.3.7(@types/react@18.3.29) + '@types/react-syntax-highlighter': + specifier: ^15.5.11 + version: 15.5.13 + '@vitejs/plugin-react': + specifier: ^4.2.1 + version: 4.7.0(vite@5.4.21(@types/node@22.7.5)) + add: + specifier: ^2.0.6 + version: 2.0.6 + autoprefixer: + specifier: ^10.4.19 + version: 10.5.0(postcss@8.5.15) + gh-pages: + specifier: ^6.1.1 + version: 6.3.0 + prettier: + specifier: ^3.2.5 + version: 3.8.3 + prettier-plugin-tailwindcss: + specifier: ^0.5.13 + version: 0.5.14(prettier@3.8.3) + tailwindcss: + specifier: ^3.4.2 + version: 3.4.19 + typescript: + specifier: ^5.8.3 + version: 5.9.3 + vite: + specifier: ^5.2.0 + version: 5.4.21(@types/node@22.7.5) + +packages: + + '@aave/account@0.2.0': + resolution: {integrity: sha512-fk9KcC0He0WNvUGytxVv4urh8F+zznm6ZdpgKnNvq9jKUpnc2HSSn7mS3n4h13h80WtF8OL6rpL50ctqqc4/Nw==} + peerDependencies: + react: 17.x || 18.x || 19.x + react-dom: 17.x || 18.x || 19.x + viem: 2.x + wagmi: 2.x + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + viem: + optional: true + wagmi: + optional: true + + '@adraffy/ens-normalize@1.10.1': + resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} + + '@adraffy/ens-normalize@1.11.1': + resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@aztec/bb.js@3.0.0-nightly.20260102': + resolution: {integrity: sha512-2SkTJw5sNyXU301OVOHOuqhd5JA3D+HE4cvKxj/hDG7rBiOi851e6swL29l2Y/UpcW2eNZMmmmk/NZgiDfDAgg==} + hasBin: true + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.3': + resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.3': + resolution: {integrity: sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.28.6': + resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.28.6': + resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': + resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.3': + resolution: {integrity: sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6': + resolution: {integrity: sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.28.6': + resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.28.6': + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.28.6': + resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.28.6': + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.29.0': + resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.28.6': + resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.28.6': + resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.28.6': + resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.28.6': + resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.28.6': + resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.28.6': + resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.28.5': + resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.28.6': + resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.28.6': + resolution: {integrity: sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.28.6': + resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.28.6': + resolution: {integrity: sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.28.6': + resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.28.6': + resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.29.4': + resolution: {integrity: sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': + resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.28.6': + resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.28.6': + resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.28.6': + resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.28.6': + resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.28.6': + resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.28.6': + resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-constant-elements@7.27.1': + resolution: {integrity: sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.28.0': + resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-development@7.27.1': + resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.28.6': + resolution: {integrity: sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-pure-annotations@7.27.1': + resolution: {integrity: sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.0': + resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.28.6': + resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.28.6': + resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.6': + resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.28.6': + resolution: {integrity: sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6': + resolution: {integrity: sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.29.5': + resolution: {integrity: sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-react@7.28.5': + resolution: {integrity: sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.28.5': + resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@base-org/account@2.4.0': + resolution: {integrity: sha512-A4Umpi8B9/pqR78D1Yoze4xHyQaujioVRqqO3d6xuDFw9VRtjg6tK3bPlwE0aW+nVH/ntllCpPa2PbI8Rnjcug==} + + '@coinbase/cdp-sdk@1.50.0': + resolution: {integrity: sha512-lKK6aC2z8q8C3IA39unNuWc8lgM0hU9mSqkdd7Bncf5xvT28f8G6upexFtJweNwxkeAJwiLSgBkwOhqMK2/OGQ==} + + '@coinbase/wallet-sdk@3.9.3': + resolution: {integrity: sha512-N/A2DRIf0Y3PHc1XAMvbBUu4zisna6qAdqABMZwBMNEfWrXpAwx16pZGkYCLGE+Rvv1edbcB2LYDRnACNcmCiw==} + + '@coinbase/wallet-sdk@4.3.6': + resolution: {integrity: sha512-4q8BNG1ViL4mSAAvPAtpwlOs1gpC+67eQtgIwNvT3xyeyFFd+guwkc8bcX5rTmQhXpqnhzC4f0obACbP9CqMSA==} + + '@crisp-e3/sdk@0.9.0': + resolution: {integrity: sha512-LpiJrks9ypj0CKrsHMoYh+TC1Xcm3dspYDWLBbnnMxZUUoFDbLO6ckgVUNh4vPQi5MwGGhTUU6X4M3lTNja92w==} + + '@crisp-e3/zk-inputs@0.9.0': + resolution: {integrity: sha512-c7/IYPRvr0/M68wfQDyNotcC71VAy+CW07KmmU8s4Lau+nHEa/jb2p6VVfQymctZ/nh3j3DzzD2yDR1OE5gfjg==} + + '@ecies/ciphers@0.2.6': + resolution: {integrity: sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g==} + engines: {bun: '>=1', deno: '>=2.7.10', node: '>=16'} + peerDependencies: + '@noble/ciphers': ^1.0.0 + + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@0.8.8': + resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} + + '@emotion/is-prop-valid@1.4.0': + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} + + '@emotion/memoize@0.7.4': + resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/stylis@0.8.5': + resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==} + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/unitless@0.7.5': + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@ethereumjs/common@3.2.0': + resolution: {integrity: sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==} + + '@ethereumjs/rlp@4.0.1': + resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==} + engines: {node: '>=14'} + hasBin: true + + '@ethereumjs/tx@4.2.0': + resolution: {integrity: sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==} + engines: {node: '>=14'} + + '@ethereumjs/util@8.1.0': + resolution: {integrity: sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==} + engines: {node: '>=14'} + + '@gemini-wallet/core@0.3.2': + resolution: {integrity: sha512-Z4aHi3ECFf5oWYWM3F1rW83GJfB9OvhBYPTmb5q+VyK3uvzvS48lwo+jwh2eOoCRWEuT/crpb9Vwp2QaS5JqgQ==} + peerDependencies: + viem: '>=2.0.0' + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@lit-labs/ssr-dom-shim@1.6.0': + resolution: {integrity: sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==} + + '@lit/reactive-element@2.1.2': + resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==} + + '@metamask/eth-json-rpc-provider@1.0.1': + resolution: {integrity: sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==} + engines: {node: '>=14.0.0'} + + '@metamask/json-rpc-engine@7.3.3': + resolution: {integrity: sha512-dwZPq8wx9yV3IX2caLi9q9xZBw2XeIoYqdyihDDDpuHVCEiqadJLwqM3zy+uwf6F1QYQ65A8aOMQg1Uw7LMLNg==} + engines: {node: '>=16.0.0'} + + '@metamask/json-rpc-engine@8.0.2': + resolution: {integrity: sha512-IoQPmql8q7ABLruW7i4EYVHWUbF74yrp63bRuXV5Zf9BQwcn5H9Ww1eLtROYvI1bUXwOiHZ6qT5CWTrDc/t/AA==} + engines: {node: '>=16.0.0'} + + '@metamask/json-rpc-middleware-stream@7.0.2': + resolution: {integrity: sha512-yUdzsJK04Ev98Ck4D7lmRNQ8FPioXYhEUZOMS01LXW8qTvPGiRVXmVltj2p4wrLkh0vW7u6nv0mNl5xzC5Qmfg==} + engines: {node: '>=16.0.0'} + + '@metamask/object-multiplex@2.1.0': + resolution: {integrity: sha512-4vKIiv0DQxljcXwfpnbsXcfa5glMj5Zg9mqn4xpIWqkv6uJ2ma5/GtUfLFSxhlxnR8asRMv8dDmWya1Tc1sDFA==} + engines: {node: ^16.20 || ^18.16 || >=20} + + '@metamask/onboarding@1.0.1': + resolution: {integrity: sha512-FqHhAsCI+Vacx2qa5mAFcWNSrTcVGMNjzxVgaX8ECSny/BJ9/vgXP9V7WF/8vb9DltPeQkxr+Fnfmm6GHfmdTQ==} + + '@metamask/providers@16.1.0': + resolution: {integrity: sha512-znVCvux30+3SaUwcUGaSf+pUckzT5ukPRpcBmy+muBLC0yaWnBcvDqGfcsw6CBIenUdFrVoAFa8B6jsuCY/a+g==} + engines: {node: ^18.18 || >=20} + + '@metamask/rpc-errors@6.4.0': + resolution: {integrity: sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==} + engines: {node: '>=16.0.0'} + + '@metamask/rpc-errors@7.0.2': + resolution: {integrity: sha512-YYYHsVYd46XwY2QZzpGeU4PSdRhHdxnzkB8piWGvJW2xbikZ3R+epAYEL4q/K8bh9JPTucsUdwRFnACor1aOYw==} + engines: {node: ^18.20 || ^20.17 || >=22} + + '@metamask/safe-event-emitter@2.0.0': + resolution: {integrity: sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==} + + '@metamask/safe-event-emitter@3.1.2': + resolution: {integrity: sha512-5yb2gMI1BDm0JybZezeoX/3XhPDOtTbcFvpTXM9kxsoZjPZFh4XciqRbpD6N86HYZqWDhEaKUDuOyR0sQHEjMA==} + engines: {node: '>=12.0.0'} + + '@metamask/sdk-analytics@0.0.5': + resolution: {integrity: sha512-fDah+keS1RjSUlC8GmYXvx6Y26s3Ax1U9hGpWb6GSY5SAdmTSIqp2CvYy6yW0WgLhnYhW+6xERuD0eVqV63QIQ==} + + '@metamask/sdk-communication-layer@0.33.1': + resolution: {integrity: sha512-0bI9hkysxcfbZ/lk0T2+aKVo1j0ynQVTuB3sJ5ssPWlz+Z3VwveCkP1O7EVu1tsVVCb0YV5WxK9zmURu2FIiaA==} + peerDependencies: + cross-fetch: ^4.0.0 + eciesjs: '*' + eventemitter2: ^6.4.9 + readable-stream: ^3.6.2 + socket.io-client: ^4.5.1 + + '@metamask/sdk-install-modal-web@0.32.1': + resolution: {integrity: sha512-MGmAo6qSjf1tuYXhCu2EZLftq+DSt5Z7fsIKr2P+lDgdTPWgLfZB1tJKzNcwKKOdf6q9Qmmxn7lJuI/gq5LrKw==} + + '@metamask/sdk@0.33.1': + resolution: {integrity: sha512-1mcOQVGr9rSrVcbKPNVzbZ8eCl1K0FATsYH3WJ/MH4WcZDWGECWrXJPNMZoEAkLxWiMe8jOQBumg2pmcDa9zpQ==} + + '@metamask/superstruct@3.2.1': + resolution: {integrity: sha512-fLgJnDOXFmuVlB38rUN5SmU7hAFQcCjrg3Vrxz67KTY7YHFnSNEKvX4avmEBdOI0yTCxZjwMCFEqsC8k2+Wd3g==} + engines: {node: '>=16.0.0'} + + '@metamask/utils@11.11.0': + resolution: {integrity: sha512-0nF2CWjWQr/m0Y2t2lJnBTU1/CZPPTvKvcESLplyWe/tyeb8zFOi/FeneDmaFnML6LYRIGZU6f+xR0jKAIUZfw==} + engines: {node: ^18.18 || ^20.14 || >=22} + + '@metamask/utils@5.0.2': + resolution: {integrity: sha512-yfmE79bRQtnMzarnKfX7AEJBwFTxvTyw3nBQlu/5rmGXrjAeAMltoGxO62TFurxrQAFMNa/fEjIHNvungZp0+g==} + engines: {node: '>=14.0.0'} + + '@metamask/utils@8.5.0': + resolution: {integrity: sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==} + engines: {node: '>=16.0.0'} + + '@metamask/utils@9.3.0': + resolution: {integrity: sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==} + engines: {node: '>=16.0.0'} + + '@motionone/animation@10.18.0': + resolution: {integrity: sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==} + + '@motionone/dom@10.12.0': + resolution: {integrity: sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==} + + '@motionone/easing@10.18.0': + resolution: {integrity: sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==} + + '@motionone/generators@10.18.0': + resolution: {integrity: sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==} + + '@motionone/types@10.17.1': + resolution: {integrity: sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==} + + '@motionone/utils@10.18.0': + resolution: {integrity: sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==} + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + cpu: [arm64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + cpu: [x64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + cpu: [arm64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + cpu: [arm] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + cpu: [x64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + cpu: [x64] + os: [win32] + + '@noble/ciphers@1.2.1': + resolution: {integrity: sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.2.0': + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + + '@noble/curves@1.4.2': + resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} + + '@noble/curves@1.8.0': + resolution: {integrity: sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.8.1': + resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.3.2': + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@noble/hashes@1.7.0': + resolution: {integrity: sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.7.1': + resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@noir-lang/acvm_js@1.0.0-beta.16': + resolution: {integrity: sha512-2xcEE0mCFaZEvVoKVVsG5DTUgj68duFosIvV0Z19Gn5YM1Ay5pHQnFBX4xfhJPA/0Ex8yMRZA/QDE4jpMz96KQ==} + + '@noir-lang/noir_js@1.0.0-beta.16': + resolution: {integrity: sha512-DHgD73WVR6ly6c+5ocrLWLooppYwTsvx4pyndloXlXm8LcJ4ouySg4dNgN8hqNLrTixqxjWu8wBy7eYiAS1iHQ==} + + '@noir-lang/noirc_abi@1.0.0-beta.16': + resolution: {integrity: sha512-4RpTEyw34ap65B7gQQLTeXoRtFACsATd+o4fgjLgvoIBiwwftcp2+MOFGJA3icRUJyBXknmlNu+HXCOI7Vl+Xg==} + + '@noir-lang/types@1.0.0-beta.16': + resolution: {integrity: sha512-QDZRdvC3NZ1USCOkmcgONoerQl5eWpl2uPreE/BN+bidgz+Q3cANW/KO9tt0ZeWWVMfLrYV2n7o6l0y8pJUV1g==} + + '@paulmillr/qr@0.2.1': + resolution: {integrity: sha512-IHnV6A+zxU7XwmKFinmYjUcwlyK9+xkG3/s9KcQhI9BjQKycrJ1JRO+FbNYPwZiPKW3je/DR0k7w8/gLa5eaxQ==} + deprecated: 'The package is now available as "qr": npm install qr' + + '@phosphor-icons/react@2.1.10': + resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} + engines: {node: '>=10'} + peerDependencies: + react: '>= 16.8' + react-dom: '>= 16.8' + + '@remix-run/router@1.23.2': + resolution: {integrity: sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==} + engines: {node: '>=14.0.0'} + + '@reown/appkit-common@1.7.8': + resolution: {integrity: sha512-ridIhc/x6JOp7KbDdwGKY4zwf8/iK8EYBl+HtWrruutSLwZyVi5P8WaZa+8iajL6LcDcDF7LoyLwMTym7SRuwQ==} + + '@reown/appkit-controllers@1.7.8': + resolution: {integrity: sha512-IdXlJlivrlj6m63VsGLsjtPHHsTWvKGVzWIP1fXZHVqmK+rZCBDjCi9j267Rb9/nYRGHWBtlFQhO8dK35WfeDA==} + + '@reown/appkit-pay@1.7.8': + resolution: {integrity: sha512-OSGQ+QJkXx0FEEjlpQqIhT8zGJKOoHzVnyy/0QFrl3WrQTjCzg0L6+i91Ad5Iy1zb6V5JjqtfIFpRVRWN4M3pw==} + + '@reown/appkit-polyfills@1.7.8': + resolution: {integrity: sha512-W/kq786dcHHAuJ3IV2prRLEgD/2iOey4ueMHf1sIFjhhCGMynMkhsOhQMUH0tzodPqUgAC494z4bpIDYjwWXaA==} + + '@reown/appkit-scaffold-ui@1.7.8': + resolution: {integrity: sha512-RCeHhAwOrIgcvHwYlNWMcIDibdI91waaoEYBGw71inE0kDB8uZbE7tE6DAXJmDkvl0qPh+DqlC4QbJLF1FVYdQ==} + + '@reown/appkit-ui@1.7.8': + resolution: {integrity: sha512-1hjCKjf6FLMFzrulhl0Y9Vb9Fu4royE+SXCPSWh4VhZhWqlzUFc7kutnZKx8XZFVQH4pbBvY62SpRC93gqoHow==} + + '@reown/appkit-utils@1.7.8': + resolution: {integrity: sha512-8X7UvmE8GiaoitCwNoB86pttHgQtzy4ryHZM9kQpvjQ0ULpiER44t1qpVLXNM4X35O0v18W0Dk60DnYRMH2WRw==} + peerDependencies: + valtio: 1.13.2 + + '@reown/appkit-wallet@1.7.8': + resolution: {integrity: sha512-kspz32EwHIOT/eg/ZQbFPxgXq0B/olDOj3YMu7gvLEFz4xyOFd/wgzxxAXkp5LbG4Cp++s/elh79rVNmVFdB9A==} + + '@reown/appkit@1.7.8': + resolution: {integrity: sha512-51kTleozhA618T1UvMghkhKfaPcc9JlKwLJ5uV+riHyvSoWPKPRIa5A6M1Wano5puNyW0s3fwywhyqTHSilkaA==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/plugin-inject@5.0.5': + resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-virtual@3.0.2': + resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.60.4': + resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.4': + resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.4': + resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.4': + resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.4': + resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.4': + resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.60.4': + resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.60.4': + resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.60.4': + resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.60.4': + resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.60.4': + resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.60.4': + resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.60.4': + resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.60.4': + resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.60.4': + resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.4': + resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.4': + resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.4': + resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.4': + resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.4': + resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} + cpu: [x64] + os: [win32] + + '@safe-global/safe-apps-provider@0.18.6': + resolution: {integrity: sha512-4LhMmjPWlIO8TTDC2AwLk44XKXaK6hfBTWyljDm0HQ6TWlOEijVWNrt2s3OCVMSxlXAcEzYfqyu1daHZooTC2Q==} + + '@safe-global/safe-apps-sdk@9.1.0': + resolution: {integrity: sha512-N5p/ulfnnA2Pi2M3YeWjULeWbjo7ei22JwU/IXnhoHzKq3pYCN6ynL9mJBOlvDVv892EgLPCWCOwQk/uBT2v0Q==} + + '@safe-global/safe-gateway-typescript-sdk@3.23.1': + resolution: {integrity: sha512-6ORQfwtEJYpalCeVO21L4XXGSdbEMfyp2hEv6cP82afKXSwvse6d3sdelgaPWUxHIsFRkWvHDdzh8IyyKHZKxw==} + engines: {node: '>=16'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + + '@scure/base@1.1.9': + resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} + + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + + '@scure/bip32@1.4.0': + resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} + + '@scure/bip32@1.6.2': + resolution: {integrity: sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==} + + '@scure/bip32@1.7.0': + resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} + + '@scure/bip39@1.3.0': + resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} + + '@scure/bip39@1.5.4': + resolution: {integrity: sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==} + + '@scure/bip39@1.6.0': + resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + + '@solana-program/system@0.10.0': + resolution: {integrity: sha512-Go+LOEZmqmNlfr+Gjy5ZWAdY5HbYzk2RBewD9QinEU/bBSzpFfzqDRT55JjFRBGJUvMgf3C2vfXEGT4i8DSI4g==} + peerDependencies: + '@solana/kit': ^5.0 + + '@solana-program/token@0.9.0': + resolution: {integrity: sha512-vnZxndd4ED4Fc56sw93cWZ2djEeeOFxtaPS8SPf5+a+JZjKA/EnKqzbE1y04FuMhIVrLERQ8uR8H2h72eZzlsA==} + peerDependencies: + '@solana/kit': ^5.0 + + '@solana/accounts@5.5.1': + resolution: {integrity: sha512-TfOY9xixg5rizABuLVuZ9XI2x2tmWUC/OoN556xwfDlhBHBjKfszicYYOyD6nbFmwTGYarCmyGIdteXxTXIdhQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/addresses@5.5.1': + resolution: {integrity: sha512-5xoah3Q9G30HQghu/9BiHLb5pzlPKRC3zydQDmE3O9H//WfayxTFppsUDCL6FjYUHqj/wzK6CWHySglc2RkpdA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/assertions@5.5.1': + resolution: {integrity: sha512-YTCSWAlGwSlVPnWtWLm3ukz81wH4j2YaCveK+TjpvUU88hTy6fmUqxi0+hvAMAe4zKXpJyj3Az7BrLJRxbIm4Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-core@5.5.1': + resolution: {integrity: sha512-TgBt//bbKBct0t6/MpA8ElaOA3sa8eYVvR7LGslCZ84WiAwwjCY0lW/lOYsFHJQzwREMdUyuEyy5YWBKtdh8Rw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-data-structures@5.5.1': + resolution: {integrity: sha512-97bJWGyUY9WvBz3mX1UV3YPWGDTez6btCfD0ip3UVEXJbItVuUiOkzcO5iFDUtQT5riKT6xC+Mzl+0nO76gd0w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-numbers@5.5.1': + resolution: {integrity: sha512-rllMIZAHqmtvC0HO/dc/21wDuWaD0B8Ryv8o+YtsICQBuiL/0U4AGwH7Pi5GNFySYk0/crSuwfIqQFtmxNSPFw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-strings@5.5.1': + resolution: {integrity: sha512-7klX4AhfHYA+uKKC/nxRGP2MntbYQCR3N6+v7bk1W/rSxYuhNmt+FN8aoThSZtWIKwN6BEyR1167ka8Co1+E7A==} + engines: {node: '>=20.18.0'} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: ^5.0.0 + peerDependenciesMeta: + fastestsmallesttextencoderdecoder: + optional: true + typescript: + optional: true + + '@solana/codecs@5.5.1': + resolution: {integrity: sha512-Vea29nJub/bXjfzEV7ZZQ/PWr1pYLZo3z0qW0LQL37uKKVzVFRQlwetd7INk3YtTD3xm9WUYr7bCvYUk3uKy2g==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/errors@5.5.1': + resolution: {integrity: sha512-vFO3p+S7HoyyrcAectnXbdsMfwUzY2zYFUc2DEe5BwpiE9J1IAxPBGjOWO6hL1bbYdBrlmjNx8DXCslqS+Kcmg==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/fast-stable-stringify@5.5.1': + resolution: {integrity: sha512-Ni7s2FN33zTzhTFgRjEbOVFO+UAmK8qi3Iu0/GRFYK4jN696OjKHnboSQH/EacQ+yGqS54bfxf409wU5dsLLCw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/functional@5.5.1': + resolution: {integrity: sha512-tTHoJcEQq3gQx5qsdsDJ0LEJeFzwNpXD80xApW9o/PPoCNimI3SALkZl+zNW8VnxRrV3l3yYvfHWBKe/X3WG3w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/instruction-plans@5.5.1': + resolution: {integrity: sha512-7z3CB7YMcFKuVvgcnNY8bY6IsZ8LG61Iytbz7HpNVGX2u1RthOs1tRW8luTzSG1MPL0Ox7afyAVMYeFqSPHnaQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/instructions@5.5.1': + resolution: {integrity: sha512-h0G1CG6S+gUUSt0eo6rOtsaXRBwCq1+Js2a+Ps9Bzk9q7YHNFA75/X0NWugWLgC92waRp66hrjMTiYYnLBoWOQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/keys@5.5.1': + resolution: {integrity: sha512-KRD61cL7CRL+b4r/eB9dEoVxIf/2EJ1Pm1DmRYhtSUAJD2dJ5Xw8QFuehobOGm9URqQ7gaQl+Fkc1qvDlsWqKg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/kit@5.5.1': + resolution: {integrity: sha512-irKUGiV2yRoyf+4eGQ/ZeCRxa43yjFEL1DUI5B0DkcfZw3cr0VJtVJnrG8OtVF01vT0OUfYOcUn6zJW5TROHvQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/nominal-types@5.5.1': + resolution: {integrity: sha512-I1ImR+kfrLFxN5z22UDiTWLdRZeKtU0J/pkWkO8qm/8WxveiwdIv4hooi8pb6JnlR4mSrWhq0pCIOxDYrL9GIQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/offchain-messages@5.5.1': + resolution: {integrity: sha512-g+xHH95prTU+KujtbOzj8wn+C7ZNoiLhf3hj6nYq3MTyxOXtBEysguc97jJveUZG0K97aIKG6xVUlMutg5yxhw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/options@5.5.1': + resolution: {integrity: sha512-eo971c9iLNLmk+yOFyo7yKIJzJ/zou6uKpy6mBuyb/thKtS/haiKIc3VLhyTXty3OH2PW8yOlORJnv4DexJB8A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/plugin-core@5.5.1': + resolution: {integrity: sha512-VUZl30lDQFJeiSyNfzU1EjYt2QZvoBFKEwjn1lilUJw7KgqD5z7mbV7diJhT+dLFs36i0OsjXvq5kSygn8YJ3A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/programs@5.5.1': + resolution: {integrity: sha512-7U9kn0Jsx1NuBLn5HRTFYh78MV4XN145Yc3WP/q5BlqAVNlMoU9coG5IUTJIG847TUqC1lRto3Dnpwm6T4YRpA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/promises@5.5.1': + resolution: {integrity: sha512-T9lfuUYkGykJmppEcssNiCf6yiYQxJkhiLPP+pyAc2z84/7r3UVIb2tNJk4A9sucS66pzJnVHZKcZVGUUp6wzA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-api@5.5.1': + resolution: {integrity: sha512-XWOQQPhKl06Vj0xi3RYHAc6oEQd8B82okYJ04K7N0Vvy3J4PN2cxeK7klwkjgavdcN9EVkYCChm2ADAtnztKnA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-parsed-types@5.5.1': + resolution: {integrity: sha512-HEi3G2nZqGEsa3vX6U0FrXLaqnUCg4SKIUrOe8CezD+cSFbRTOn3rCLrUmJrhVyXlHoQVaRO9mmeovk31jWxJg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-spec-types@5.5.1': + resolution: {integrity: sha512-6OFKtRpIEJQs8Jb2C4OO8KyP2h2Hy1MFhatMAoXA+0Ik8S3H+CicIuMZvGZ91mIu/tXicuOOsNNLu3HAkrakrw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-spec@5.5.1': + resolution: {integrity: sha512-m3LX2bChm3E3by4mQrH4YwCAFY57QBzuUSWqlUw7ChuZ+oLLOq7b2czi4i6L4Vna67j3eCmB3e+4tqy1j5wy7Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-api@5.5.1': + resolution: {integrity: sha512-5Oi7k+GdeS8xR2ly1iuSFkAv6CZqwG0Z6b1QZKbEgxadE1XGSDrhM2cn59l+bqCozUWCqh4c/A2znU/qQjROlw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-channel-websocket@5.5.1': + resolution: {integrity: sha512-7tGfBBrYY8TrngOyxSHoCU5shy86iA9SRMRrPSyBhEaZRAk6dnbdpmUTez7gtdVo0BCvh9nzQtUycKWSS7PnFQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-spec@5.5.1': + resolution: {integrity: sha512-iq+rGq5fMKP3/mKHPNB6MC8IbVW41KGZg83Us/+LE3AWOTWV1WT20KT2iH1F1ik9roi42COv/TpoZZvhKj45XQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions@5.5.1': + resolution: {integrity: sha512-CTMy5bt/6mDh4tc6vUJms9EcuZj3xvK0/xq8IQ90rhkpYvate91RjBP+egvjgSayUg9yucU9vNuUpEjz4spM7w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-transformers@5.5.1': + resolution: {integrity: sha512-OsWqLCQdcrRJKvHiMmwFhp9noNZ4FARuMkHT5us3ustDLXaxOjF0gfqZLnMkulSLcKt7TGXqMhBV+HCo7z5M8Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-transport-http@5.5.1': + resolution: {integrity: sha512-yv8GoVSHqEV0kUJEIhkdOVkR2SvJ6yoWC51cJn2rSV7plr6huLGe0JgujCmB7uZhhaLbcbP3zxXxu9sOjsi7Fg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-types@5.5.1': + resolution: {integrity: sha512-bibTFQ7PbHJJjGJPmfYC2I+/5CRFS4O2p9WwbFraX1Keeel+nRrt/NBXIy8veP5AEn2sVJIyJPpWBRpCx1oATA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc@5.5.1': + resolution: {integrity: sha512-ku8zTUMrkCWci66PRIBC+1mXepEnZH/q1f3ck0kJZ95a06bOTl5KU7HeXWtskkyefzARJ5zvCs54AD5nxjQJ+A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/signers@5.5.1': + resolution: {integrity: sha512-FY0IVaBT2kCAze55vEieR6hag4coqcuJ31Aw3hqRH7mv6sV8oqwuJmUrx+uFwOp1gwd5OEAzlv6N4hOOple4sQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/subscribable@5.5.1': + resolution: {integrity: sha512-9K0PsynFq0CsmK1CDi5Y2vUIJpCqkgSS5yfDN0eKPgHqEptLEaia09Kaxc90cSZDZU5mKY/zv1NBmB6Aro9zQQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/sysvars@5.5.1': + resolution: {integrity: sha512-k3Quq87Mm+geGUu1GWv6knPk0ALsfY6EKSJGw9xUJDHzY/RkYSBnh0RiOrUhtFm2TDNjOailg8/m0VHmi3reFA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-confirmation@5.5.1': + resolution: {integrity: sha512-j4mKlYPHEyu+OD7MBt3jRoX4ScFgkhZC6H65on4Fux6LMScgivPJlwnKoZMnsgxFgWds0pl+BYzSiALDsXlYtw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-messages@5.5.1': + resolution: {integrity: sha512-aXyhMCEaAp3M/4fP0akwBBQkFPr4pfwoC5CLDq999r/FUwDax2RE/h4Ic7h2Xk+JdcUwsb+rLq85Y52hq84XvQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transactions@5.5.1': + resolution: {integrity: sha512-8hHtDxtqalZ157pnx6p8k10D7J/KY/biLzfgh9R09VNLLY3Fqi7kJvJCr7M2ik3oRll56pxhraAGCC9yIT6eOA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0': + resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0': + resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0': + resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-svg-component@8.0.0': + resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-preset@8.1.0': + resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/core@8.1.0': + resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} + engines: {node: '>=14'} + + '@svgr/hast-util-to-babel-ast@8.0.0': + resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} + engines: {node: '>=14'} + + '@svgr/plugin-jsx@8.1.0': + resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + + '@svgr/plugin-svgo@8.1.0': + resolution: {integrity: sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + + '@svgr/rollup@8.1.0': + resolution: {integrity: sha512-0XR1poYvPQoPpmfDYLEqUGu5ePAQ4pdgN3VFsZBNAeze7qubVpsIY1o1R6PZpKep/DKu33GSm2NhwpCLkMs2Cw==} + engines: {node: '>=14'} + + '@swc/core-darwin-arm64@1.15.40': + resolution: {integrity: sha512-PaYyclfmQ++77D8ityYvmmVzHv9aG8ROwt2GfG6/ccloy4Hgf80qtOnzb9VYvPsUT7Ty1uhuDRhv3XYpf62qhQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.15.40': + resolution: {integrity: sha512-HbbPzvfLBUXjIB1Ezks+//lNUjmLjfyd63XSwprJgrZaXYdm70kohXPJUWdqKZozolFxbPaO+xtBaiUp6BoueA==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.15.40': + resolution: {integrity: sha512-SlRZsCjOCPR2LvFs0Ri/Xrx/5o5TCt8vl4gW6mX1hEZOG0a625RxzRHpHdAQNGykmAN/7IeaFAJG+QnNmxlHcA==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.15.40': + resolution: {integrity: sha512-Q8byxJt2fh8CR3EUX6snBpy47AoBVm+In/+Z3rjDHMjC38ZvR9/gtUUNCT0tfrn4EdVsO8/QPi59nxrxvqxvBQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.15.40': + resolution: {integrity: sha512-4z0MgHU+7M0pZDqBN1El7mFXDI1SBwinfcUkAyA4v8QrhOIUOZltySt2aStQLZGrdXVXM4Y4ylfiTC04ED+MoQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-ppc64-gnu@1.15.40': + resolution: {integrity: sha512-fLI4iUgeSZu0eRWUXwe6YzPFx9gHbFiPkl8Rp3mJfP8OpNR3nTQCGPvHdDh9xniW7mVvgMY4ni7A4VzqI1KrpA==} + engines: {node: '>=10'} + cpu: [ppc64] + os: [linux] + + '@swc/core-linux-s390x-gnu@1.15.40': + resolution: {integrity: sha512-YqeKMAb7d4nQSGMJQ454IlaCENpzcDqhvBE9+CPfdnYpnUXxd+BSrB6Xk0YjW8UyoEhUj4p6quATCxbsp6J3jg==} + engines: {node: '>=10'} + cpu: [s390x] + os: [linux] + + '@swc/core-linux-x64-gnu@1.15.40': + resolution: {integrity: sha512-7HOuS1iGcme/j/TuL1TfmmLGiMQrjv/GmjyZeydl00FKPtpGXEldwqfI56xgd1YzrzoB2svWjxbGGyQ0TEASxg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.15.40': + resolution: {integrity: sha512-h4kZYHc7dpc9P9u4brRJaS8Pl7tPVHAeiLSzw7T5RfIJgAoSdaCMKzI/2Uay9gFhaw8uyCDl0L5q37r0EpAfIA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.15.40': + resolution: {integrity: sha512-+mQgKZXSj6mV38Zh05QaxSjUDmGP/R2JWlXZTDLSPkDzHU6p3GxN9eeSf5dfyDVU86946fmCvSzyl/ucImx8+A==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.15.40': + resolution: {integrity: sha512-yvwdPLGd25mcj/mNatjNQ0lZujtQD6psH3v9PNmMb+fSzjbNG8KIDxjFWrcV+fsFVLOkyOmdJsFmX7NAFjVyPw==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.15.40': + resolution: {integrity: sha512-OXtKsLU1bVtInzzDEAY2sYiF/rl4tvAnLLLpuMp3HzAOQZ5A+i69AKDhA1YLQTaMAqO3vzyYNVAYVRMPtSYD4w==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.15.40': + resolution: {integrity: sha512-2kwzJikRvgtNAG7MwVZY2vEzZjTxKIq5jXOihuSV/8U+Hej8Va22t65aKnJZs3P+NwojZvR8Mf8kyM7O+V8sQg==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/types@0.1.26': + resolution: {integrity: sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==} + + '@swc/wasm@1.15.40': + resolution: {integrity: sha512-FVS3SEJXBxjpxVUGSzaTaCdJjnXUalRftA/6hILMAJIcYHBoiBfJlxuH6s47iajlAJZP25e5Kf4HNHvvwyOEgw==} + + '@tailwindcss/typography@0.5.19': + resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + + '@tanstack/query-core@5.100.14': + resolution: {integrity: sha512-5X41dGpxgeaHISCRW2oYwcSycZeULZzAunaudXT9ov1KOTj9xwt0CH6hbwqP1/z74ZWF7rYFnDpyYH07XFcZew==} + + '@tanstack/react-query@5.100.14': + resolution: {integrity: sha512-oOr6aRdSFEwWhzxEkD/9ZcItM3+LjBSkeVmadWKwUssAHTsqd/7bOjWrX4AbvEkoEhgAxzN0Xk6H/aYzXiYBAw==} + peerDependencies: + react: ^18 || ^19 + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/hast@2.3.10': + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/lodash@4.17.24': + resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@22.7.5': + resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + peerDependencies: + '@types/react': ^18.0.0 + + '@types/react-syntax-highlighter@15.5.13': + resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} + + '@types/react@18.3.29': + resolution: {integrity: sha512-ch0qJdr2JY0r04NXSprbK6TXOgnaJ1Tz23fm5W+z0/CBah6BSBc3n96h7K9GOtwh0HrilNWHIBzE1Ko4Dcw/Wg==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@ungap/structured-clone@1.3.1': + resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@wagmi/connectors@6.2.0': + resolution: {integrity: sha512-2NfkbqhNWdjfibb4abRMrn7u6rPjEGolMfApXss6HCDVt9AW2oVC6k8Q5FouzpJezElxLJSagWz9FW1zaRlanA==} + peerDependencies: + '@wagmi/core': 2.22.1 + typescript: '>=5.0.4' + viem: 2.x + peerDependenciesMeta: + typescript: + optional: true + + '@wagmi/core@2.22.1': + resolution: {integrity: sha512-cG/xwQWsBEcKgRTkQVhH29cbpbs/TdcUJVFXCyri3ZknxhMyGv0YEjTcrNpRgt2SaswL1KrvslSNYKKo+5YEAg==} + peerDependencies: + '@tanstack/query-core': '>=5.0.0' + typescript: '>=5.0.4' + viem: 2.x + peerDependenciesMeta: + '@tanstack/query-core': + optional: true + typescript: + optional: true + + '@walletconnect/core@2.21.0': + resolution: {integrity: sha512-o6R7Ua4myxR8aRUAJ1z3gT9nM+jd2B2mfamu6arzy1Cc6vi10fIwFWb6vg3bC8xJ6o9H3n/cN5TOW3aA9Y1XVw==} + engines: {node: '>=18'} + + '@walletconnect/core@2.21.1': + resolution: {integrity: sha512-Tp4MHJYcdWD846PH//2r+Mu4wz1/ZU/fr9av1UWFiaYQ2t2TPLDiZxjLw54AAEpMqlEHemwCgiRiAmjR1NDdTQ==} + engines: {node: '>=18'} + + '@walletconnect/environment@1.0.1': + resolution: {integrity: sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==} + + '@walletconnect/ethereum-provider@2.21.1': + resolution: {integrity: sha512-SSlIG6QEVxClgl1s0LMk4xr2wg4eT3Zn/Hb81IocyqNSGfXpjtawWxKxiC5/9Z95f1INyBD6MctJbL/R1oBwIw==} + + '@walletconnect/events@1.0.1': + resolution: {integrity: sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==} + + '@walletconnect/heartbeat@1.2.2': + resolution: {integrity: sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==} + + '@walletconnect/jsonrpc-http-connection@1.0.8': + resolution: {integrity: sha512-+B7cRuaxijLeFDJUq5hAzNyef3e3tBDIxyaCNmFtjwnod5AGis3RToNqzFU33vpVcxFhofkpE7Cx+5MYejbMGw==} + + '@walletconnect/jsonrpc-provider@1.0.14': + resolution: {integrity: sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==} + + '@walletconnect/jsonrpc-types@1.0.4': + resolution: {integrity: sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==} + + '@walletconnect/jsonrpc-utils@1.0.8': + resolution: {integrity: sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==} + + '@walletconnect/jsonrpc-ws-connection@1.0.16': + resolution: {integrity: sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q==} + + '@walletconnect/keyvaluestorage@1.1.1': + resolution: {integrity: sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==} + peerDependencies: + '@react-native-async-storage/async-storage': 1.x + peerDependenciesMeta: + '@react-native-async-storage/async-storage': + optional: true + + '@walletconnect/logger@2.1.2': + resolution: {integrity: sha512-aAb28I3S6pYXZHQm5ESB+V6rDqIYfsnHaQyzFbwUUBFY4H0OXx/YtTl8lvhUNhMMfb9UxbwEBS253TlXUYJWSw==} + + '@walletconnect/relay-api@1.0.11': + resolution: {integrity: sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==} + + '@walletconnect/relay-auth@1.1.0': + resolution: {integrity: sha512-qFw+a9uRz26jRCDgL7Q5TA9qYIgcNY8jpJzI1zAWNZ8i7mQjaijRnWFKsCHAU9CyGjvt6RKrRXyFtFOpWTVmCQ==} + + '@walletconnect/safe-json@1.0.2': + resolution: {integrity: sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==} + + '@walletconnect/sign-client@2.21.0': + resolution: {integrity: sha512-z7h+PeLa5Au2R591d/8ZlziE0stJvdzP9jNFzFolf2RG/OiXulgFKum8PrIyXy+Rg2q95U9nRVUF9fWcn78yBA==} + + '@walletconnect/sign-client@2.21.1': + resolution: {integrity: sha512-QaXzmPsMnKGV6tc4UcdnQVNOz4zyXgarvdIQibJ4L3EmLat73r5ZVl4c0cCOcoaV7rgM9Wbphgu5E/7jNcd3Zg==} + + '@walletconnect/time@1.0.2': + resolution: {integrity: sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==} + + '@walletconnect/types@2.21.0': + resolution: {integrity: sha512-ll+9upzqt95ZBWcfkOszXZkfnpbJJ2CmxMfGgE5GmhdxxxCcO5bGhXkI+x8OpiS555RJ/v/sXJYMSOLkmu4fFw==} + + '@walletconnect/types@2.21.1': + resolution: {integrity: sha512-UeefNadqP6IyfwWC1Yi7ux+ljbP2R66PLfDrDm8izmvlPmYlqRerJWJvYO4t0Vvr9wrG4Ko7E0c4M7FaPKT/sQ==} + + '@walletconnect/universal-provider@2.21.0': + resolution: {integrity: sha512-mtUQvewt+X0VBQay/xOJBvxsB3Xsm1lTwFjZ6WUwSOTR1X+FNb71hSApnV5kbsdDIpYPXeQUbGt2se1n5E5UBg==} + + '@walletconnect/universal-provider@2.21.1': + resolution: {integrity: sha512-Wjx9G8gUHVMnYfxtasC9poGm8QMiPCpXpbbLFT+iPoQskDDly8BwueWnqKs4Mx2SdIAWAwuXeZ5ojk5qQOxJJg==} + + '@walletconnect/utils@2.21.0': + resolution: {integrity: sha512-zfHLiUoBrQ8rP57HTPXW7rQMnYxYI4gT9yTACxVW6LhIFROTF6/ytm5SKNoIvi4a5nX5dfXG4D9XwQUCu8Ilig==} + + '@walletconnect/utils@2.21.1': + resolution: {integrity: sha512-VPZvTcrNQCkbGOjFRbC24mm/pzbRMUq2DSQoiHlhh0X1U7ZhuIrzVtAoKsrzu6rqjz0EEtGxCr3K1TGRqDG4NA==} + + '@walletconnect/window-getters@1.0.1': + resolution: {integrity: sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==} + + '@walletconnect/window-metadata@1.0.1': + resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==} + + '@zk-kit/lean-imt@2.2.4': + resolution: {integrity: sha512-uoRl99DID9Z5EuhfecDuIGP0KLrGEndVH0+texwVQBmlXTvve+grkKT4w06C+VFzRmfYNVuRo9tXvqIzQ3dqPA==} + + '@zk-kit/utils@1.4.1': + resolution: {integrity: sha512-IoXzy3ElfkvRfC/s6uk3eFQO8uxkCdAOVxKlJlaEjM7pgckX5AK46Q+7n5yMQD/v/o6OL0mMtet065o5EpUocA==} + + abitype@1.0.6: + resolution: {integrity: sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + abitype@1.0.8: + resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + abitype@1.1.0: + resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + abitype@1.2.3: + resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + abitype@1.2.4: + resolution: {integrity: sha512-dpKH+N27vRjarMVTFFkeY445VTKftzGWpL0FiT7xmVmzQRKazZexzC5uHG0f6XKsVLAuUlndnbGau6lRejClxg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + add@2.0.6: + resolution: {integrity: sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q==} + + aes-js@4.0.0-beta.5: + resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + asn1.js@4.10.1: + resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + + assert@2.1.0: + resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} + + async-mutex@0.2.6: + resolution: {integrity: sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + autoprefixer@10.5.0: + resolution: {integrity: sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axios-retry@4.5.0: + resolution: {integrity: sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==} + peerDependencies: + axios: 0.x || 1.x + + axios@1.13.2: + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + + axios@1.16.0: + resolution: {integrity: sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + babel-plugin-polyfill-corejs2@0.4.17: + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.14.2: + resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.8: + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-styled-components@2.3.0: + resolution: {integrity: sha512-nP/y6PbBqS/qtKROnJCgpGo8hYUzlBAVXN1QAjSBANL6vZiQXPQN7FYW/nUwoxY7nZhBEGm9T5tjL9gbzwulDw==} + peerDependencies: + '@babel/core': ^7.0.0 + styled-components: '>= 2' + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + base-x@5.0.1: + resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.10.32: + resolution: {integrity: sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==} + engines: {node: '>=6.0.0'} + hasBin: true + + big.js@6.2.2: + resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bn.js@4.12.3: + resolution: {integrity: sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==} + + bn.js@5.2.3: + resolution: {integrity: sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + + browser-resolve@2.0.0: + resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} + + browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + + browserify-cipher@1.0.1: + resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + + browserify-des@1.0.2: + resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + + browserify-rsa@4.1.1: + resolution: {integrity: sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==} + engines: {node: '>= 0.10'} + + browserify-sign@4.2.5: + resolution: {integrity: sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==} + engines: {node: '>= 0.10'} + + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + + buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bufferutil@4.1.0: + resolution: {integrity: sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==} + engines: {node: '>=6.14.2'} + + builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + + caniuse-lite@1.0.30001793: + resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + charenc@0.0.2: + resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + cipher-base@1.0.7: + resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==} + engines: {node: '>= 0.10'} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comlink@4.4.2: + resolution: {integrity: sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==} + + comma-separated-tokens@1.0.8: + resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + commander@14.0.2: + resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + engines: {node: '>=20'} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + connectkit@1.9.2: + resolution: {integrity: sha512-ZaYdL2UgHdcVy5j7Cn5C6KUtA5Ev3LEc3mPAzEoH3V3hdu3CdC8jgFTFsgInTXXpsdNpwTEEDPnP245ok7wOYg==} + engines: {node: '>=12.4'} + peerDependencies: + '@tanstack/react-query': '>=5.0.0' + react: 17.x || 18.x + react-dom: 17.x || 18.x + viem: 2.x + wagmi: 2.x + + console-browserify@1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + + constants-browserify@1.0.0: + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@1.2.3: + resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} + + core-js-compat@3.49.0: + resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + create-ecdh@4.0.4: + resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + + create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + + create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + cross-fetch@4.1.0: + resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} + + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + crypt@0.0.2: + resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + + crypto-browserify@3.12.1: + resolution: {integrity: sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==} + engines: {node: '>= 0.10'} + + css-color-keywords@1.0.0: + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-to-react-native@3.2.0: + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + derive-valtio@0.1.0: + resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==} + peerDependencies: + valtio: '*' + + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-browser@5.3.0: + resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + diffie-hellman@5.0.3: + resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domain-browser@4.22.0: + resolution: {integrity: sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==} + engines: {node: '>=10'} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexify@4.1.3: + resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} + + eciesjs@0.4.18: + resolution: {integrity: sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ==} + engines: {bun: '>=1', deno: '>=2', node: '>=16'} + + electron-to-chromium@1.5.361: + resolution: {integrity: sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==} + + elliptic@6.6.1: + resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + + email-addresses@5.0.0: + resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encode-utf8@1.0.3: + resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + engine.io-client@6.6.5: + resolution: {integrity: sha512-QCwxUDULPlXv8F6tqMMKx5dNkTe6OaBYRMPYeXKBlyOoKvAmE0ac6pW7fFhSscJ/5SI7666/U/B+MElbsrJlIg==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-toolkit@1.33.0: + resolution: {integrity: sha512-X13Q/ZSc+vsO1q600bvNK4bxgXMkHcf//RxCmYDaRY5DAcT+eoXjY5hoAPGMdRnWQjvyLEcyauG3b6hz76LNqg==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eth-block-tracker@7.1.0: + resolution: {integrity: sha512-8YdplnuE1IK4xfqpf4iU7oBxnOYAc35934o083G8ao+8WM8QQtt/mVlAY6yIAdY1eMeLqg4Z//PZjJGmWGPMRg==} + engines: {node: '>=14.0.0'} + + eth-json-rpc-filters@6.0.1: + resolution: {integrity: sha512-ITJTvqoCw6OVMLs7pI8f4gG92n/St6x80ACtHodeS+IXmO0w+t1T5OOzfSt7KLSMLRkVUoexV7tztLgDxg+iig==} + engines: {node: '>=14.0.0'} + + eth-query@2.1.2: + resolution: {integrity: sha512-srES0ZcvwkR/wd5OQBRA1bIJMww1skfGS0s8wlwK3/oNP4+wnds60krvu5R1QbpRQjMmpG5OMIWro5s7gvDPsA==} + + eth-rpc-errors@4.0.3: + resolution: {integrity: sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg==} + + ethereum-cryptography@2.2.1: + resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==} + + ethers@6.13.5: + resolution: {integrity: sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ==} + engines: {node: '>=14.0.0'} + + ethers@6.16.0: + resolution: {integrity: sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==} + engines: {node: '>=14.0.0'} + + eventemitter2@6.4.9: + resolution: {integrity: sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extension-port-stream@3.0.0: + resolution: {integrity: sha512-an2S5quJMiy5bnZKEf6AkfH/7r8CzHvhchU40gxN+OM6HPhe7Z9T1FUychcf2M9PpPOO0Hf7BAEfJkw2TDIBDw==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fault@1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + filename-reserved-regex@2.0.0: + resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==} + engines: {node: '>=4'} + + filenamify@4.3.0: + resolution: {integrity: sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==} + engines: {node: '>=8'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + filter-obj@1.1.0: + resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} + engines: {node: '>=0.10.0'} + + find-cache-dir@3.3.2: + resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} + engines: {node: '>=8'} + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + framer-motion@6.5.1: + resolution: {integrity: sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==} + peerDependencies: + react: '>=16.8 || ^17.0.0 || ^18.0.0' + react-dom: '>=16.8 || ^17.0.0 || ^18.0.0' + + framesync@6.0.1: + resolution: {integrity: sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==} + + fs-extra@11.3.5: + resolution: {integrity: sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==} + engines: {node: '>=14.14'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gh-pages@6.3.0: + resolution: {integrity: sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==} + engines: {node: '>=10'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + h3@1.15.11: + resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hash-base@3.0.5: + resolution: {integrity: sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==} + engines: {node: '>= 0.10'} + + hash-base@3.1.2: + resolution: {integrity: sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==} + engines: {node: '>= 0.8'} + + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + engines: {node: '>= 0.4'} + + hast-util-parse-selector@2.2.5: + resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@6.0.0: + resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + + hey-listen@1.0.8: + resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + highlightjs-vue@1.0.0: + resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} + + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + hono@4.12.22: + resolution: {integrity: sha512-7fvVPbB92zNRsQke+uiRGwtTuef0tB2Dg4hWxYfFNvkQhIltWoyi0ONReM5LWA+jJWS3nfT5lTq+qbsIpX0IQw==} + engines: {node: '>=16.9.0'} + + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + + https-browserify@1.0.0: + resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} + + idb-keyval@6.2.1: + resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} + + idb-keyval@6.2.4: + resolution: {integrity: sha512-D/NzHWUmYJGXi++z67aMSrnisb9A3621CyRK5G89JyTlN13C8xf0g04DLxUKMufPem3e3L2JAXR6Z00OWy183Q==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-nan@1.3.2: + resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-retry-allowed@2.2.0: + resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==} + engines: {node: '>=10'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isomorphic-timers-promises@1.0.1: + resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} + engines: {node: '>=10'} + + isows@1.0.6: + resolution: {integrity: sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==} + peerDependencies: + ws: '*' + + isows@1.0.7: + resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + peerDependencies: + ws: '*' + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-rpc-engine@6.1.0: + resolution: {integrity: sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ==} + engines: {node: '>=10.0.0'} + + json-rpc-random-id@1.0.1: + resolution: {integrity: sha512-RJ9YYNCkhVDBuP4zN5BBtYAzEl03yq/jIIsyif0JY9qyJuQQZNeDK7anAPKKlyEtLSj2s8h6hNh2F8zO5q7ScA==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + + keccak@3.0.4: + resolution: {integrity: sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==} + engines: {node: '>=10.0.0'} + + keyvaluestorage-interface@1.0.0: + resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lit-element@4.2.2: + resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} + + lit-html@3.3.3: + resolution: {integrity: sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==} + + lit@3.3.0: + resolution: {integrity: sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lowlight@1.20.0: + resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + + lru-cache@11.5.0: + resolution: {integrity: sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + + md5@2.3.0: + resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micro-ftch@0.3.1: + resolution: {integrity: sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + miller-rabin@4.0.1: + resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} + hasBin: true + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + + mipd@0.0.7: + resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msgpackr-extract@3.0.3: + resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + hasBin: true + + msgpackr@1.11.12: + resolution: {integrity: sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==} + + multiformats@9.9.0: + resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-addon-api@2.0.2: + resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + node-mock-http@1.0.4: + resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + + node-releases@2.0.46: + resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} + engines: {node: '>=18'} + + node-stdlib-browser@1.3.1: + resolution: {integrity: sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw==} + engines: {node: '>=10'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + obj-multiplex@1.0.0: + resolution: {integrity: sha512-0GNJAOsHoBHeNTvl5Vt6IWnpUEcc3uSRxzBri7EDyIcMgYvnY2JL2qdeV5zTMjWQX5OHcD5amcW2HFfDh0gjIA==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + on-exit-leak-free@0.2.0: + resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + openapi-fetch@0.13.8: + resolution: {integrity: sha512-yJ4QKRyNxE44baQ9mY5+r/kAzZ8yXMemtNAOFwOzRXJscdjSxxzWSNlyBAr+o5JjkUw9Lc3W7OIoca0cY3PYnQ==} + + openapi-typescript-helpers@0.0.15: + resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==} + + os-browserify@0.3.0: + resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + + ox@0.14.22: + resolution: {integrity: sha512-nb5msL8qWbPglhIfZbGJAfw3cqiJjFMiWmACt7kgyWtLib12tcctbHufMT9Hb0Lr6Pt4k9I3dbpueTpbhvbqvA==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.6.7: + resolution: {integrity: sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.6.9: + resolution: {integrity: sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.7.1: + resolution: {integrity: sha512-+k9fY9PRNuAMHRFIUbiK9Nt5seYHHzSQs9Bj+iMETcGtlpS7SmBzcGSVUQO3+nqGLEiNK4598pHNFlVRaZbRsg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.9.17: + resolution: {integrity: sha512-rKAnhzhRU3Xh3hiko+i1ZxywZ55eWQzeS/Q4HRKLx2PqfHOolisZHErSsJVipGlmQKHW5qwOED/GighEw9dbLg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.9.6: + resolution: {integrity: sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-asn1@5.1.9: + resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==} + engines: {node: '>= 0.10'} + + parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pbkdf2@3.1.5: + resolution: {integrity: sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==} + engines: {node: '>= 0.10'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + + pify@5.0.0: + resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} + engines: {node: '>=10'} + + pino-abstract-transport@0.5.0: + resolution: {integrity: sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==} + + pino-std-serializers@4.0.0: + resolution: {integrity: sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==} + + pino@7.11.0: + resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==} + hasBin: true + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pkg-dir@5.0.0: + resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} + engines: {node: '>=10'} + + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + + pony-cause@2.1.11: + resolution: {integrity: sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==} + engines: {node: '>=12.0.0'} + + popmotion@11.0.3: + resolution: {integrity: sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==} + + porto@0.2.35: + resolution: {integrity: sha512-gu9FfjjvvYBgQXUHWTp6n3wkTxVtEcqFotM7i3GEZeoQbvLGbssAicCz6hFZ8+xggrJWwi/RLmbwNra50SMmUQ==} + hasBin: true + peerDependencies: + '@tanstack/react-query': '>=5.59.0' + '@wagmi/core': '>=2.16.3' + expo-auth-session: '>=7.0.8' + expo-crypto: '>=15.0.7' + expo-web-browser: '>=15.0.8' + react: '>=18' + react-native: '>=0.81.4' + typescript: '>=5.4.0' + viem: '>=2.37.0' + wagmi: '>=2.0.0' + peerDependenciesMeta: + '@tanstack/react-query': + optional: true + expo-auth-session: + optional: true + expo-crypto: + optional: true + expo-web-browser: + optional: true + react: + optional: true + react-native: + optional: true + typescript: + optional: true + wagmi: + optional: true + + poseidon-lite@0.3.0: + resolution: {integrity: sha512-ilJj4MIve4uBEG7SrtPqUUNkvpJ/pLVbndxa0WvebcQqeIhe+h72JR4g0EvwchUzm9sOQDlOjiDNmRAgxNZl4A==} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + + preact@10.24.2: + resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==} + + preact@10.29.2: + resolution: {integrity: sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==} + + prettier-plugin-tailwindcss@0.5.14: + resolution: {integrity: sha512-Puaz+wPUAhFp8Lo9HuciYKM2Y2XExESjeT+9NQoVFXZsPPnc9VYss2SpxdQ6vbatmt8/4+SN0oe0I1cPDABg9Q==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig-melody': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig-melody': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@3.8.3: + resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} + engines: {node: '>=14'} + hasBin: true + + prismjs@1.27.0: + resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} + engines: {node: '>=6'} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process-warning@1.0.0: + resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + property-information@5.6.0: + resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + proxy-compare@2.6.0: + resolution: {integrity: sha512-8xuCeM3l8yqdmbPoYeLbrAXCBWu19XEYc5/F28f5qOaoAIMyfmBUkl5axiK+x9olUvRlcekvnm98AP9RDngOIw==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + public-encrypt@4.0.3: + resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + + qrcode@1.5.3: + resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==} + engines: {node: '>=10.13.0'} + hasBin: true + + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + + qs@6.15.2: + resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} + engines: {node: '>=0.6'} + + query-string@7.1.3: + resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} + engines: {node: '>=6'} + + querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-markdown@9.1.0: + resolution: {integrity: sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-router-dom@6.30.3: + resolution: {integrity: sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.30.3: + resolution: {integrity: sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + + react-syntax-highlighter@15.6.6: + resolution: {integrity: sha512-DgXrc+AZF47+HvAPEmn7Ua/1p10jNoVZVI/LoPiYdtY+OM+/nG5yefLHKJwdKqY1adMuHFbeyBaG9j64ML7vTw==} + peerDependencies: + react: '>= 0.14.0' + + react-transition-state@1.1.5: + resolution: {integrity: sha512-ITY2mZqc2dWG2eitJkYNdcSFW8aKeOlkL2A/vowRrLL8GH3J6Re/SpD/BLvQzrVOTqjsP0b5S9N10vgNNzwMUQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + react-use-measure@2.1.7: + resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==} + peerDependencies: + react: '>=16.13' + react-dom: '>=16.13' + peerDependenciesMeta: + react-dom: + optional: true + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + real-require@0.1.0: + resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==} + engines: {node: '>= 12.13.0'} + + refractor@3.6.0: + resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.1: + resolution: {integrity: sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==} + hasBin: true + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + ripemd160@2.0.3: + resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} + engines: {node: '>= 0.8'} + + rollup@4.60.4: + resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.1: + resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + engines: {node: '>=10'} + hasBin: true + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + sha.js@2.4.12: + resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} + engines: {node: '>= 0.10'} + hasBin: true + + shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + + socket.io-client@4.8.3: + resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.6: + resolution: {integrity: sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==} + engines: {node: '>=10.0.0'} + + sonic-boom@2.8.0: + resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@1.1.5: + resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + split-on-first@1.1.0: + resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} + engines: {node: '>=6'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + + stream-http@3.2.0: + resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} + + stream-shift@1.0.3: + resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + + strict-uri-encode@2.0.0: + resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} + engines: {node: '>=4'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-outer@1.0.1: + resolution: {integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==} + engines: {node: '>=0.10.0'} + + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + style-value-types@5.0.0: + resolution: {integrity: sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==} + + styled-components@5.3.11: + resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==} + engines: {node: '>=10'} + peerDependencies: + react: '>= 16.8.0' + react-dom: '>= 16.8.0' + react-is: '>= 16.8.0' + + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + superstruct@1.0.4: + resolution: {integrity: sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==} + engines: {node: '>=14.0.0'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + + svgo@3.3.3: + resolution: {integrity: sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==} + engines: {node: '>=14.0.0'} + hasBin: true + + tailwindcss@3.4.19: + resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} + engines: {node: '>=14.0.0'} + hasBin: true + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + thread-stream@0.15.2: + resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==} + + timers-browserify@2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + to-buffer@1.2.2: + resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} + engines: {node: '>= 0.4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trim-repeated@1.0.0: + resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==} + engines: {node: '>=0.10.0'} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tty-browserify@0.0.1: + resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + uint8arrays@3.1.0: + resolution: {integrity: sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + undici-types@7.25.0: + resolution: {integrity: sha512-AXNgS1Byr27fTI+2bsPEkV9CxkT8H6xNyRI68b3TatlZo3RkzlqQBLL+w7SmGPVpokjHbcuNVQUWE7FRTg+LRA==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unstorage@1.17.5: + resolution: {integrity: sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1 || ^2 || ^3 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + url@0.11.4: + resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} + engines: {node: '>= 0.4'} + + use-sync-external-store@1.2.0: + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + use-sync-external-store@1.4.0: + resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + utf-8-validate@5.0.10: + resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} + engines: {node: '>=6.14.2'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + + valtio@1.13.2: + resolution: {integrity: sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=16.8' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + viem@2.23.2: + resolution: {integrity: sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + viem@2.30.6: + resolution: {integrity: sha512-N3vGy3pZ+EVgQRuWqQhZPFXxQE8qMRrBd3uM+KLc1Ub2w6+vkyr7umeWQCM4c+wlsCdByUgh2630MDMLquMtpg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + viem@2.38.6: + resolution: {integrity: sha512-aqO6P52LPXRjdnP6rl5Buab65sYa4cZ6Cpn+k4OLOzVJhGIK8onTVoKMFMT04YjDfyDICa/DZyV9HmvLDgcjkw==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + viem@2.50.4: + resolution: {integrity: sha512-rf98F4s3Vlb+uJZEKfay3IbBw3CNCbVtx5Y3UIljlO2tSX420g/J0WQSYsjzBSasUFgxgsXabji14O9kGbiqgg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + vite-plugin-node-polyfills@0.22.0: + resolution: {integrity: sha512-F+G3LjiGbG8QpbH9bZ//GSBr9i1InSTkaulfUHFa9jkLqVGORFBoqc2A/Yu5Mmh1kNAbiAeKeK+6aaQUf3x0JA==} + peerDependencies: + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + + vite-plugin-top-level-await@1.6.0: + resolution: {integrity: sha512-bNhUreLamTIkoulCR9aDXbTbhLk6n1YE8NJUTTxl5RYskNRtzOR0ASzSjBVRtNdjIfngDXo11qOsybGLNsrdww==} + peerDependencies: + vite: '>=2.8' + + vite-tsconfig-paths@4.3.2: + resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vm-browserify@1.1.2: + resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + + wagmi@2.19.5: + resolution: {integrity: sha512-RQUfKMv6U+EcSNNGiPbdkDtJwtuFxZWLmvDiQmjjBgkuPulUwDJsKhi7gjynzJdsx2yDqhHCXkKsbbfbIsHfcQ==} + peerDependencies: + '@tanstack/react-query': '>=5.0.0' + react: '>=18' + typescript: '>=5.0.4' + viem: 2.x + peerDependenciesMeta: + typescript: + optional: true + + webextension-polyfill@0.10.0: + resolution: {integrity: sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@7.5.11: + resolution: {integrity: sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.2: + resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.20.1: + resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@1.10.3: + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + engines: {node: '>= 6'} + + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zustand@5.0.0: + resolution: {integrity: sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zustand@5.0.13: + resolution: {integrity: sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zustand@5.0.3: + resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@aave/account@0.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))': + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: 2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + + '@adraffy/ens-normalize@1.10.1': {} + + '@adraffy/ens-normalize@1.11.1': {} + + '@alloc/quick-lru@5.2.0': {} + + '@aztec/bb.js@3.0.0-nightly.20260102': + dependencies: + comlink: 4.4.2 + commander: 12.1.0 + idb-keyval: 6.2.4 + msgpackr: 1.11.12 + pako: 2.1.0 + tslib: 2.8.1 + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.3': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@5.5.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + debug: 4.4.3(supports-color@5.5.0) + lodash.debounce: 4.0.8 + resolve: 1.22.12 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.29.0(supports-color@5.5.0) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.28.6(supports-color@5.5.0)': + dependencies: + '@babel/traverse': 7.29.0(supports-color@5.5.0) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6(supports-color@5.5.0) + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.29.0(supports-color@5.5.0) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6(supports-color@5.5.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/template': 7.28.6 + + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.29.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-constant-elements@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.28.6(supports-color@5.5.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/preset-env@7.29.5(@babel/core@7.29.0)': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.3(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.0) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-systemjs': 7.29.4(@babel/core@7.29.0) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.0) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.49.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/types': 7.29.0 + esutils: 2.0.3 + + '@babel/preset-react@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.29.2': {} + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0(supports-color@5.5.0)': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@base-org/account@2.4.0(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@coinbase/cdp-sdk': 1.50.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) + preact: 10.24.2 + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.3(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - debug + - fastestsmallesttextencoderdecoder + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod + + '@coinbase/cdp-sdk@1.50.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana-program/system': 0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@solana-program/token': 0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@solana/kit': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + abitype: 1.0.6(typescript@5.9.3)(zod@3.25.76) + axios: 1.16.0 + axios-retry: 4.5.0(axios@1.16.0) + bs58: 6.0.0 + jose: 6.2.3 + md5: 2.3.0 + uncrypto: 0.1.3 + viem: 2.50.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - debug + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + + '@coinbase/wallet-sdk@3.9.3': + dependencies: + bn.js: 5.2.3 + buffer: 6.0.3 + clsx: 1.2.1 + eth-block-tracker: 7.1.0 + eth-json-rpc-filters: 6.0.1 + eventemitter3: 5.0.4 + keccak: 3.0.4 + preact: 10.29.2 + sha.js: 2.4.12 + transitivePeerDependencies: + - supports-color + + '@coinbase/wallet-sdk@4.3.6(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) + preact: 10.24.2 + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.3(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod + + '@crisp-e3/sdk@0.9.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@aztec/bb.js': 3.0.0-nightly.20260102 + '@crisp-e3/zk-inputs': 0.9.0 + '@noir-lang/noir_js': 1.0.0-beta.16 + '@zk-kit/lean-imt': 2.2.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) + poseidon-lite: 0.3.0 + viem: 2.30.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@crisp-e3/zk-inputs@0.9.0': {} + + '@ecies/ciphers@0.2.6(@noble/ciphers@1.3.0)': + dependencies: + '@noble/ciphers': 1.3.0 + + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.28.6(supports-color@5.5.0) + '@babel/runtime': 7.29.2 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@0.8.8': + dependencies: + '@emotion/memoize': 0.7.4 + optional: true + + '@emotion/is-prop-valid@1.4.0': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.7.4': + optional: true + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@18.3.29)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.29 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.2.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/stylis@0.8.5': {} + + '@emotion/unitless@0.10.0': {} + + '@emotion/unitless@0.7.5': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@ethereumjs/common@3.2.0': + dependencies: + '@ethereumjs/util': 8.1.0 + crc-32: 1.2.2 + + '@ethereumjs/rlp@4.0.1': {} + + '@ethereumjs/tx@4.2.0': + dependencies: + '@ethereumjs/common': 3.2.0 + '@ethereumjs/rlp': 4.0.1 + '@ethereumjs/util': 8.1.0 + ethereum-cryptography: 2.2.1 + + '@ethereumjs/util@8.1.0': + dependencies: + '@ethereumjs/rlp': 4.0.1 + ethereum-cryptography: 2.2.1 + micro-ftch: 0.3.1 + + '@gemini-wallet/core@0.3.2(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + dependencies: + '@metamask/rpc-errors': 7.0.2 + eventemitter3: 5.0.1 + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@lit-labs/ssr-dom-shim@1.6.0': {} + + '@lit/reactive-element@2.1.2': + dependencies: + '@lit-labs/ssr-dom-shim': 1.6.0 + + '@metamask/eth-json-rpc-provider@1.0.1': + dependencies: + '@metamask/json-rpc-engine': 7.3.3 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 5.0.2 + transitivePeerDependencies: + - supports-color + + '@metamask/json-rpc-engine@7.3.3': + dependencies: + '@metamask/rpc-errors': 6.4.0 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 8.5.0 + transitivePeerDependencies: + - supports-color + + '@metamask/json-rpc-engine@8.0.2': + dependencies: + '@metamask/rpc-errors': 6.4.0 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 8.5.0 + transitivePeerDependencies: + - supports-color + + '@metamask/json-rpc-middleware-stream@7.0.2': + dependencies: + '@metamask/json-rpc-engine': 8.0.2 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 8.5.0 + readable-stream: 3.6.2 + transitivePeerDependencies: + - supports-color + + '@metamask/object-multiplex@2.1.0': + dependencies: + once: 1.4.0 + readable-stream: 3.6.2 + + '@metamask/onboarding@1.0.1': + dependencies: + bowser: 2.14.1 + + '@metamask/providers@16.1.0': + dependencies: + '@metamask/json-rpc-engine': 8.0.2 + '@metamask/json-rpc-middleware-stream': 7.0.2 + '@metamask/object-multiplex': 2.1.0 + '@metamask/rpc-errors': 6.4.0 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 8.5.0 + detect-browser: 5.3.0 + extension-port-stream: 3.0.0 + fast-deep-equal: 3.1.3 + is-stream: 2.0.1 + readable-stream: 3.6.2 + webextension-polyfill: 0.10.0 + transitivePeerDependencies: + - supports-color + + '@metamask/rpc-errors@6.4.0': + dependencies: + '@metamask/utils': 9.3.0 + fast-safe-stringify: 2.1.1 + transitivePeerDependencies: + - supports-color + + '@metamask/rpc-errors@7.0.2': + dependencies: + '@metamask/utils': 11.11.0 + fast-safe-stringify: 2.1.1 + transitivePeerDependencies: + - supports-color + + '@metamask/safe-event-emitter@2.0.0': {} + + '@metamask/safe-event-emitter@3.1.2': {} + + '@metamask/sdk-analytics@0.0.5': + dependencies: + openapi-fetch: 0.13.8 + + '@metamask/sdk-communication-layer@0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.18)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + dependencies: + '@metamask/sdk-analytics': 0.0.5 + bufferutil: 4.1.0 + cross-fetch: 4.1.0 + date-fns: 2.30.0 + debug: 4.3.4 + eciesjs: 0.4.18 + eventemitter2: 6.4.9 + readable-stream: 3.6.2 + socket.io-client: 4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + utf-8-validate: 5.0.10 + uuid: 8.3.2 + transitivePeerDependencies: + - supports-color + + '@metamask/sdk-install-modal-web@0.32.1': + dependencies: + '@paulmillr/qr': 0.2.1 + + '@metamask/sdk@0.33.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + dependencies: + '@babel/runtime': 7.29.2 + '@metamask/onboarding': 1.0.1 + '@metamask/providers': 16.1.0 + '@metamask/sdk-analytics': 0.0.5 + '@metamask/sdk-communication-layer': 0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.18)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@metamask/sdk-install-modal-web': 0.32.1 + '@paulmillr/qr': 0.2.1 + bowser: 2.14.1 + cross-fetch: 4.1.0 + debug: 4.3.4 + eciesjs: 0.4.18 + eth-rpc-errors: 4.0.3 + eventemitter2: 6.4.9 + obj-multiplex: 1.0.0 + pump: 3.0.4 + readable-stream: 3.6.2 + socket.io-client: 4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + tslib: 2.8.1 + util: 0.12.5 + uuid: 8.3.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + + '@metamask/superstruct@3.2.1': {} + + '@metamask/utils@11.11.0': + dependencies: + '@ethereumjs/tx': 4.2.0 + '@metamask/superstruct': 3.2.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@types/debug': 4.1.13 + '@types/lodash': 4.17.24 + debug: 4.4.3(supports-color@5.5.0) + lodash: 4.18.1 + pony-cause: 2.1.11 + semver: 7.8.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + + '@metamask/utils@5.0.2': + dependencies: + '@ethereumjs/tx': 4.2.0 + '@types/debug': 4.1.13 + debug: 4.4.3(supports-color@5.5.0) + semver: 7.8.1 + superstruct: 1.0.4 + transitivePeerDependencies: + - supports-color + + '@metamask/utils@8.5.0': + dependencies: + '@ethereumjs/tx': 4.2.0 + '@metamask/superstruct': 3.2.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@types/debug': 4.1.13 + debug: 4.3.4 + pony-cause: 2.1.11 + semver: 7.8.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + + '@metamask/utils@9.3.0': + dependencies: + '@ethereumjs/tx': 4.2.0 + '@metamask/superstruct': 3.2.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@types/debug': 4.1.13 + debug: 4.3.4 + pony-cause: 2.1.11 + semver: 7.8.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + + '@motionone/animation@10.18.0': + dependencies: + '@motionone/easing': 10.18.0 + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + tslib: 2.8.1 + + '@motionone/dom@10.12.0': + dependencies: + '@motionone/animation': 10.18.0 + '@motionone/generators': 10.18.0 + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + hey-listen: 1.0.8 + tslib: 2.8.1 + + '@motionone/easing@10.18.0': + dependencies: + '@motionone/utils': 10.18.0 + tslib: 2.8.1 + + '@motionone/generators@10.18.0': + dependencies: + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + tslib: 2.8.1 + + '@motionone/types@10.17.1': {} + + '@motionone/utils@10.18.0': + dependencies: + '@motionone/types': 10.17.1 + hey-listen: 1.0.8 + tslib: 2.8.1 + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + + '@noble/ciphers@1.2.1': {} + + '@noble/ciphers@1.3.0': {} + + '@noble/curves@1.2.0': + dependencies: + '@noble/hashes': 1.3.2 + + '@noble/curves@1.4.2': + dependencies: + '@noble/hashes': 1.4.0 + + '@noble/curves@1.8.0': + dependencies: + '@noble/hashes': 1.7.0 + + '@noble/curves@1.8.1': + dependencies: + '@noble/hashes': 1.7.1 + + '@noble/curves@1.9.1': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/hashes@1.3.2': {} + + '@noble/hashes@1.4.0': {} + + '@noble/hashes@1.7.0': {} + + '@noble/hashes@1.7.1': {} + + '@noble/hashes@1.8.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@noir-lang/acvm_js@1.0.0-beta.16': {} + + '@noir-lang/noir_js@1.0.0-beta.16': + dependencies: + '@noir-lang/acvm_js': 1.0.0-beta.16 + '@noir-lang/noirc_abi': 1.0.0-beta.16 + '@noir-lang/types': 1.0.0-beta.16 + pako: 2.1.0 + + '@noir-lang/noirc_abi@1.0.0-beta.16': + dependencies: + '@noir-lang/types': 1.0.0-beta.16 + + '@noir-lang/types@1.0.0-beta.16': {} + + '@paulmillr/qr@0.2.1': {} + + '@phosphor-icons/react@2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@remix-run/router@1.23.2': {} + + '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@reown/appkit-controllers@1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 1.13.2(@types/react@18.3.29)(react@18.3.1) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-pay@1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1))(zod@3.25.76) + lit: 3.3.0 + valtio: 1.13.2(@types/react@18.3.29)(react@18.3.1) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-polyfills@1.7.8': + dependencies: + buffer: 6.0.3 + + '@reown/appkit-scaffold-ui@1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + lit: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - valtio + - zod + + '@reown/appkit-ui@1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + lit: 3.3.0 + qrcode: 1.5.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-utils@1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.7.8 + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@walletconnect/logger': 2.1.2 + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 1.13.2(@types/react@18.3.29)(react@18.3.1) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-wallet@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) + '@reown/appkit-polyfills': 1.7.8 + '@walletconnect/logger': 2.1.2 + zod: 3.22.4 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + + '@reown/appkit@1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.7.8 + '@reown/appkit-scaffold-ui': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@walletconnect/types': 2.21.0 + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + bs58: 6.0.0 + valtio: 1.13.2(@types/react@18.3.29)(react@18.3.1) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/plugin-inject@5.0.5(rollup@4.60.4)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.60.4) + estree-walker: 2.0.2 + magic-string: 0.30.21 + optionalDependencies: + rollup: 4.60.4 + + '@rollup/plugin-virtual@3.0.2(rollup@4.60.4)': + optionalDependencies: + rollup: 4.60.4 + + '@rollup/pluginutils@5.3.0(rollup@4.60.4)': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.4 + optionalDependencies: + rollup: 4.60.4 + + '@rollup/rollup-android-arm-eabi@4.60.4': + optional: true + + '@rollup/rollup-android-arm64@4.60.4': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.4': + optional: true + + '@rollup/rollup-darwin-x64@4.60.4': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.4': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.4': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.4': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.4': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.4': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.4': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.4': + optional: true + + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@safe-global/safe-gateway-typescript-sdk': 3.23.1 + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@safe-global/safe-gateway-typescript-sdk@3.23.1': {} + + '@scure/base@1.1.9': {} + + '@scure/base@1.2.6': {} + + '@scure/bip32@1.4.0': + dependencies: + '@noble/curves': 1.4.2 + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.9 + + '@scure/bip32@1.6.2': + dependencies: + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/base': 1.2.6 + + '@scure/bip32@1.7.0': + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/bip39@1.3.0': + dependencies: + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.9 + + '@scure/bip39@1.5.4': + dependencies: + '@noble/hashes': 1.7.1 + '@scure/base': 1.2.6 + + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@socket.io/component-emitter@3.1.2': {} + + '@solana-program/system@0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + + '@solana-program/token@0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + + '@solana/accounts@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/addresses@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/assertions': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/assertions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-core@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-data-structures@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-numbers@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-strings@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/options': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/errors@5.5.1(typescript@5.9.3)': + dependencies: + chalk: 5.6.2 + commander: 14.0.2 + optionalDependencies: + typescript: 5.9.3 + + '@solana/fast-stable-stringify@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/functional@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/instruction-plans@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/instructions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/keys@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/assertions': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/accounts': 5.5.1(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instruction-plans': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/offchain-messages': 5.5.1(typescript@5.9.3) + '@solana/plugin-core': 5.5.1(typescript@5.9.3) + '@solana/programs': 5.5.1(typescript@5.9.3) + '@solana/rpc': 5.5.1(typescript@5.9.3) + '@solana/rpc-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/signers': 5.5.1(typescript@5.9.3) + '@solana/sysvars': 5.5.1(typescript@5.9.3) + '@solana/transaction-confirmation': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + + '@solana/nominal-types@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/offchain-messages@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/options@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/plugin-core@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/programs@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/promises@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-api@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-parsed-types@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-spec-types@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-spec@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-subscriptions-api@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-subscriptions-channel-websocket@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) + ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@solana/rpc-subscriptions-spec@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-subscriptions@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-channel-websocket': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + + '@solana/rpc-transformers@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-transport-http@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + undici-types: 7.25.0 + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-types@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/rpc-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-transport-http': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/signers@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/offchain-messages': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/subscribable@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/sysvars@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/accounts': 5.5.1(typescript@5.9.3) + '@solana/codecs': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/transaction-confirmation@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + + '@solana/transaction-messages@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/transactions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-preset@8.1.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.29.0) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.29.0) + + '@svgr/core@8.1.0(typescript@5.9.3)': + dependencies: + '@babel/core': 7.29.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.0) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.9.3) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@svgr/hast-util-to-babel-ast@8.0.0': + dependencies: + '@babel/types': 7.29.0 + entities: 4.5.0 + + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))': + dependencies: + '@babel/core': 7.29.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.0) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3)': + dependencies: + '@svgr/core': 8.1.0(typescript@5.9.3) + cosmiconfig: 8.3.6(typescript@5.9.3) + deepmerge: 4.3.1 + svgo: 3.3.3 + transitivePeerDependencies: + - typescript + + '@svgr/rollup@8.1.0(rollup@4.60.4)(typescript@5.9.3)': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-constant-elements': 7.27.1(@babel/core@7.29.0) + '@babel/preset-env': 7.29.5(@babel/core@7.29.0) + '@babel/preset-react': 7.28.5(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) + '@rollup/pluginutils': 5.3.0(rollup@4.60.4) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3) + transitivePeerDependencies: + - rollup + - supports-color + - typescript + + '@swc/core-darwin-arm64@1.15.40': + optional: true + + '@swc/core-darwin-x64@1.15.40': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.15.40': + optional: true + + '@swc/core-linux-arm64-gnu@1.15.40': + optional: true + + '@swc/core-linux-arm64-musl@1.15.40': + optional: true + + '@swc/core-linux-ppc64-gnu@1.15.40': + optional: true + + '@swc/core-linux-s390x-gnu@1.15.40': + optional: true + + '@swc/core-linux-x64-gnu@1.15.40': + optional: true + + '@swc/core-linux-x64-musl@1.15.40': + optional: true + + '@swc/core-win32-arm64-msvc@1.15.40': + optional: true + + '@swc/core-win32-ia32-msvc@1.15.40': + optional: true + + '@swc/core-win32-x64-msvc@1.15.40': + optional: true + + '@swc/core@1.15.40': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.26 + optionalDependencies: + '@swc/core-darwin-arm64': 1.15.40 + '@swc/core-darwin-x64': 1.15.40 + '@swc/core-linux-arm-gnueabihf': 1.15.40 + '@swc/core-linux-arm64-gnu': 1.15.40 + '@swc/core-linux-arm64-musl': 1.15.40 + '@swc/core-linux-ppc64-gnu': 1.15.40 + '@swc/core-linux-s390x-gnu': 1.15.40 + '@swc/core-linux-x64-gnu': 1.15.40 + '@swc/core-linux-x64-musl': 1.15.40 + '@swc/core-win32-arm64-msvc': 1.15.40 + '@swc/core-win32-ia32-msvc': 1.15.40 + '@swc/core-win32-x64-msvc': 1.15.40 + + '@swc/counter@0.1.3': {} + + '@swc/types@0.1.26': + dependencies: + '@swc/counter': 0.1.3 + + '@swc/wasm@1.15.40': {} + + '@tailwindcss/typography@0.5.19(tailwindcss@3.4.19)': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.19 + + '@tanstack/query-core@5.100.14': {} + + '@tanstack/react-query@5.100.14(react@18.3.1)': + dependencies: + '@tanstack/query-core': 5.100.14 + react: 18.3.1 + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.9 + + '@types/estree@1.0.8': {} + + '@types/estree@1.0.9': {} + + '@types/hast@2.3.10': + dependencies: + '@types/unist': 2.0.11 + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/lodash@4.17.24': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + + '@types/node@22.7.5': + dependencies: + undici-types: 6.19.8 + + '@types/parse-json@4.0.2': {} + + '@types/prop-types@15.7.15': {} + + '@types/react-dom@18.3.7(@types/react@18.3.29)': + dependencies: + '@types/react': 18.3.29 + + '@types/react-syntax-highlighter@15.5.13': + dependencies: + '@types/react': 18.3.29 + + '@types/react@18.3.29': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.2.3 + + '@types/trusted-types@2.0.7': {} + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@ungap/structured-clone@1.3.1': {} + + '@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.7.5))': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 5.4.21(@types/node@22.7.5) + transitivePeerDependencies: + - supports-color + + '@wagmi/connectors@6.2.0(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76)': + dependencies: + '@base-org/account': 2.4.0(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@3.25.76) + '@coinbase/wallet-sdk': 4.3.6(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@3.25.76) + '@gemini-wallet/core': 0.3.2(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@metamask/sdk': 0.33.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + cbw-sdk: '@coinbase/wallet-sdk@3.9.3' + porto: 0.2.35(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/react-query' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - expo-auth-session + - expo-crypto + - expo-web-browser + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - react-native + - supports-color + - uploadthing + - use-sync-external-store + - utf-8-validate + - wagmi + - zod + + '@wagmi/core@2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + dependencies: + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.9.3) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.0(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) + optionalDependencies: + '@tanstack/query-core': 5.100.14 + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/react' + - immer + - react + - use-sync-external-store + + '@walletconnect/core@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.33.0 + events: 3.3.0 + uint8arrays: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/core@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.33.0 + events: 3.3.0 + uint8arrays: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/environment@1.0.1': + dependencies: + tslib: 1.14.1 + + '@walletconnect/ethereum-provider@2.21.1(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.1 + '@walletconnect/universal-provider': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/events@1.0.1': + dependencies: + keyvaluestorage-interface: 1.0.0 + tslib: 1.14.1 + + '@walletconnect/heartbeat@1.2.2': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/time': 1.0.2 + events: 3.3.0 + + '@walletconnect/jsonrpc-http-connection@1.0.8': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + cross-fetch: 3.2.0 + events: 3.3.0 + transitivePeerDependencies: + - encoding + + '@walletconnect/jsonrpc-provider@1.0.14': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + + '@walletconnect/jsonrpc-types@1.0.4': + dependencies: + events: 3.3.0 + keyvaluestorage-interface: 1.0.0 + + '@walletconnect/jsonrpc-utils@1.0.8': + dependencies: + '@walletconnect/environment': 1.0.1 + '@walletconnect/jsonrpc-types': 1.0.4 + tslib: 1.14.1 + + '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + ws: 7.5.11(bufferutil@4.1.0)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@walletconnect/keyvaluestorage@1.1.1': + dependencies: + '@walletconnect/safe-json': 1.0.2 + idb-keyval: 6.2.4 + unstorage: 1.17.5(idb-keyval@6.2.4) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/logger@2.1.2': + dependencies: + '@walletconnect/safe-json': 1.0.2 + pino: 7.11.0 + + '@walletconnect/relay-api@1.0.11': + dependencies: + '@walletconnect/jsonrpc-types': 1.0.4 + + '@walletconnect/relay-auth@1.1.0': + dependencies: + '@noble/curves': 1.8.0 + '@noble/hashes': 1.7.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + uint8arrays: 3.1.0 + + '@walletconnect/safe-json@1.0.2': + dependencies: + tslib: 1.14.1 + + '@walletconnect/sign-client@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/sign-client@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/time@1.0.2': + dependencies: + tslib: 1.14.1 + + '@walletconnect/types@2.21.0': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/types@2.21.1': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/utils@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.0 + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/utils@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.0 + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/window-getters@1.0.1': + dependencies: + tslib: 1.14.1 + + '@walletconnect/window-metadata@1.0.1': + dependencies: + '@walletconnect/window-getters': 1.0.1 + tslib: 1.14.1 + + '@zk-kit/lean-imt@2.2.4(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + dependencies: + '@zk-kit/utils': 1.4.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@zk-kit/utils@1.4.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + dependencies: + buffer: 6.0.3 + ethers: 6.13.5(bufferutil@4.1.0)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + abitype@1.0.6(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + abitype@1.0.8(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + abitype@1.1.0(typescript@5.9.3)(zod@3.22.4): + optionalDependencies: + typescript: 5.9.3 + zod: 3.22.4 + + abitype@1.1.0(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + abitype@1.2.3(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + abitype@1.2.4(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + abitype@1.2.4(typescript@5.9.3)(zod@4.4.3): + optionalDependencies: + typescript: 5.9.3 + zod: 4.4.3 + + add@2.0.6: {} + + aes-js@4.0.0-beta.5: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + arg@5.0.2: {} + + argparse@2.0.1: {} + + array-union@2.1.0: {} + + asn1.js@4.10.1: + dependencies: + bn.js: 4.12.3 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + assert@2.1.0: + dependencies: + call-bind: 1.0.9 + is-nan: 1.3.2 + object-is: 1.1.6 + object.assign: 4.1.7 + util: 0.12.5 + + async-mutex@0.2.6: + dependencies: + tslib: 2.8.1 + + async@3.2.6: {} + + asynckit@0.4.0: {} + + atomic-sleep@1.0.0: {} + + autoprefixer@10.5.0(postcss@8.5.15): + dependencies: + browserslist: 4.28.2 + caniuse-lite: 1.0.30001793 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axios-retry@4.5.0(axios@1.16.0): + dependencies: + axios: 1.16.0 + is-retry-allowed: 2.2.0 + + axios@1.13.2: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + axios@1.16.0: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.29.2 + cosmiconfig: 7.1.0 + resolve: 1.22.12 + + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0): + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + babel-plugin-styled-components@2.3.0(@babel/core@7.29.0)(styled-components@5.3.11(@babel/core@7.29.0)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1))(supports-color@5.5.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.28.6(supports-color@5.5.0) + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + picomatch: 4.0.4 + styled-components: 5.3.11(@babel/core@7.29.0)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1) + transitivePeerDependencies: + - supports-color + + bail@2.0.2: {} + + base-x@5.0.1: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.10.32: {} + + big.js@6.2.2: {} + + binary-extensions@2.3.0: {} + + bn.js@4.12.3: {} + + bn.js@5.2.3: {} + + boolbase@1.0.0: {} + + bowser@2.14.1: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + brorand@1.1.0: {} + + browser-resolve@2.0.0: + dependencies: + resolve: 1.22.12 + + browserify-aes@1.2.0: + dependencies: + buffer-xor: 1.0.3 + cipher-base: 1.0.7 + create-hash: 1.2.0 + evp_bytestokey: 1.0.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-cipher@1.0.1: + dependencies: + browserify-aes: 1.2.0 + browserify-des: 1.0.2 + evp_bytestokey: 1.0.3 + + browserify-des@1.0.2: + dependencies: + cipher-base: 1.0.7 + des.js: 1.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-rsa@4.1.1: + dependencies: + bn.js: 5.2.3 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + browserify-sign@4.2.5: + dependencies: + bn.js: 5.2.3 + browserify-rsa: 4.1.1 + create-hash: 1.2.0 + create-hmac: 1.1.7 + elliptic: 6.6.1 + inherits: 2.0.4 + parse-asn1: 5.1.9 + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + + browserify-zlib@0.2.0: + dependencies: + pako: 1.0.11 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.32 + caniuse-lite: 1.0.30001793 + electron-to-chromium: 1.5.361 + node-releases: 2.0.46 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + bs58@6.0.0: + dependencies: + base-x: 5.0.1 + + buffer-xor@1.0.3: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bufferutil@4.1.0: + dependencies: + node-gyp-build: 4.8.4 + + builtin-status-codes@3.0.0: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camelcase-css@2.0.1: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + camelize@1.0.1: {} + + caniuse-lite@1.0.30001793: {} + + ccount@2.0.1: {} + + chalk@5.6.2: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@1.1.4: {} + + character-entities-legacy@3.0.0: {} + + character-entities@1.2.4: {} + + character-entities@2.0.2: {} + + character-reference-invalid@1.1.4: {} + + character-reference-invalid@2.0.1: {} + + charenc@0.0.2: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + cipher-base@1.0.7: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + clsx@1.2.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comlink@4.4.2: {} + + comma-separated-tokens@1.0.8: {} + + comma-separated-tokens@2.0.3: {} + + commander@12.1.0: {} + + commander@13.1.0: {} + + commander@14.0.2: {} + + commander@4.1.1: {} + + commander@7.2.0: {} + + commondir@1.0.1: {} + + connectkit@1.9.2(@babel/core@7.29.0)(@tanstack/react-query@5.100.14(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)): + dependencies: + '@aave/account': 0.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + '@tanstack/react-query': 5.100.14(react@18.3.1) + buffer: 6.0.3 + detect-browser: 5.3.0 + framer-motion: 6.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + qrcode: 1.5.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-state: 1.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-use-measure: 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + resize-observer-polyfill: 1.5.1 + styled-components: 5.3.11(@babel/core@7.29.0)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: 2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + transitivePeerDependencies: + - '@babel/core' + - react-is + + console-browserify@1.2.0: {} + + constants-browserify@1.0.0: {} + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cookie-es@1.2.3: {} + + core-js-compat@3.49.0: + dependencies: + browserslist: 4.28.2 + + core-util-is@1.0.3: {} + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.3 + + cosmiconfig@8.3.6(typescript@5.9.3): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.9.3 + + crc-32@1.2.2: {} + + create-ecdh@4.0.4: + dependencies: + bn.js: 4.12.3 + elliptic: 6.6.1 + + create-hash@1.2.0: + dependencies: + cipher-base: 1.0.7 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.3 + sha.js: 2.4.12 + + create-hmac@1.1.7: + dependencies: + cipher-base: 1.0.7 + create-hash: 1.2.0 + inherits: 2.0.4 + ripemd160: 2.0.3 + safe-buffer: 5.2.1 + sha.js: 2.4.12 + + create-require@1.1.1: {} + + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + cross-fetch@4.1.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + crypt@0.0.2: {} + + crypto-browserify@3.12.1: + dependencies: + browserify-cipher: 1.0.1 + browserify-sign: 4.2.5 + create-ecdh: 4.0.4 + create-hash: 1.2.0 + create-hmac: 1.1.7 + diffie-hellman: 5.0.3 + hash-base: 3.0.5 + inherits: 2.0.4 + pbkdf2: 3.1.5 + public-encrypt: 4.0.3 + randombytes: 2.1.0 + randomfill: 1.0.4 + + css-color-keywords@1.0.0: {} + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-to-react-native@3.2.0: + dependencies: + camelize: 1.0.1 + css-color-keywords: 1.0.0 + postcss-value-parser: 4.2.0 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + + css-what@6.2.2: {} + + cssesc@3.0.0: {} + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + csstype@3.2.3: {} + + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.29.2 + + dayjs@1.11.13: {} + + debug@4.3.4: + dependencies: + ms: 2.1.2 + + debug@4.4.3(supports-color@5.5.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 5.5.0 + + decamelize@1.2.0: {} + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + decode-uri-component@0.2.2: {} + + deepmerge@4.3.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + defu@6.1.7: {} + + delayed-stream@1.0.0: {} + + dequal@2.0.3: {} + + derive-valtio@0.1.0(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1)): + dependencies: + valtio: 1.13.2(@types/react@18.3.29)(react@18.3.1) + + des.js@1.1.0: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + destr@2.0.5: {} + + detect-browser@5.3.0: {} + + detect-libc@2.1.2: + optional: true + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + didyoumean@1.2.2: {} + + diffie-hellman@5.0.3: + dependencies: + bn.js: 4.12.3 + miller-rabin: 4.0.1 + randombytes: 2.1.0 + + dijkstrajs@1.0.3: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domain-browser@4.22.0: {} + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexify@4.1.3: + dependencies: + end-of-stream: 1.4.5 + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-shift: 1.0.3 + + eciesjs@0.4.18: + dependencies: + '@ecies/ciphers': 0.2.6(@noble/ciphers@1.3.0) + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + + electron-to-chromium@1.5.361: {} + + elliptic@6.6.1: + dependencies: + bn.js: 4.12.3 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + email-addresses@5.0.0: {} + + emoji-regex@8.0.0: {} + + encode-utf8@1.0.3: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + engine.io-client@6.6.5(bufferutil@4.1.0)(utf-8-validate@5.0.10): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3(supports-color@5.5.0) + engine.io-parser: 5.2.3 + ws: 8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + entities@4.5.0: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + + es-toolkit@1.33.0: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + estree-util-is-identifier-name@3.0.0: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + eth-block-tracker@7.1.0: + dependencies: + '@metamask/eth-json-rpc-provider': 1.0.1 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 5.0.2 + json-rpc-random-id: 1.0.1 + pify: 3.0.0 + transitivePeerDependencies: + - supports-color + + eth-json-rpc-filters@6.0.1: + dependencies: + '@metamask/safe-event-emitter': 3.1.2 + async-mutex: 0.2.6 + eth-query: 2.1.2 + json-rpc-engine: 6.1.0 + pify: 5.0.0 + + eth-query@2.1.2: + dependencies: + json-rpc-random-id: 1.0.1 + xtend: 4.0.2 + + eth-rpc-errors@4.0.3: + dependencies: + fast-safe-stringify: 2.1.1 + + ethereum-cryptography@2.2.1: + dependencies: + '@noble/curves': 1.4.2 + '@noble/hashes': 1.4.0 + '@scure/bip32': 1.4.0 + '@scure/bip39': 1.3.0 + + ethers@6.13.5(bufferutil@4.1.0)(utf-8-validate@5.0.10): + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@types/node': 22.7.5 + aes-js: 4.0.0-beta.5 + tslib: 2.7.0 + ws: 8.17.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@types/node': 22.7.5 + aes-js: 4.0.0-beta.5 + tslib: 2.7.0 + ws: 8.17.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + eventemitter2@6.4.9: {} + + eventemitter3@5.0.1: {} + + eventemitter3@5.0.4: {} + + events@3.3.0: {} + + evp_bytestokey@1.0.3: + dependencies: + md5.js: 1.3.5 + safe-buffer: 5.2.1 + + extend@3.0.2: {} + + extension-port-stream@3.0.0: + dependencies: + readable-stream: 3.6.2 + webextension-polyfill: 0.10.0 + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-redact@3.5.0: {} + + fast-safe-stringify@2.1.1: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fault@1.0.4: + dependencies: + format: 0.2.2 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + filename-reserved-regex@2.0.0: {} + + filenamify@4.3.0: + dependencies: + filename-reserved-regex: 2.0.0 + strip-outer: 1.0.1 + trim-repeated: 1.0.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + filter-obj@1.1.0: {} + + find-cache-dir@3.3.2: + dependencies: + commondir: 1.0.1 + make-dir: 3.1.0 + pkg-dir: 4.2.0 + + find-root@1.1.0: {} + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + follow-redirects@1.16.0: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.3 + mime-types: 2.1.35 + + format@0.2.2: {} + + fraction.js@5.3.4: {} + + framer-motion@6.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@motionone/dom': 10.12.0 + framesync: 6.0.1 + hey-listen: 1.0.8 + popmotion: 11.0.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + style-value-types: 5.0.0 + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 0.8.8 + + framesync@6.0.1: + dependencies: + tslib: 2.8.1 + + fs-extra@11.3.5: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + gh-pages@6.3.0: + dependencies: + async: 3.2.6 + commander: 13.1.0 + email-addresses: 5.0.0 + filenamify: 4.3.0 + find-cache-dir: 3.3.2 + fs-extra: 11.3.5 + globby: 11.1.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globrex@0.1.2: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + h3@1.15.11: + dependencies: + cookie-es: 1.2.3 + crossws: 0.3.5 + defu: 6.1.7 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.4 + radix3: 1.1.2 + ufo: 1.6.4 + uncrypto: 0.1.3 + + has-flag@3.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hash-base@3.0.5: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + + hash-base@3.1.2: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + hash.js@1.1.7: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + hasown@2.0.3: + dependencies: + function-bind: 1.1.2 + + hast-util-parse-selector@2.2.5: {} + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@6.0.0: + dependencies: + '@types/hast': 2.3.10 + comma-separated-tokens: 1.0.8 + hast-util-parse-selector: 2.2.5 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + + hey-listen@1.0.8: {} + + highlight.js@10.7.3: {} + + highlightjs-vue@1.0.0: {} + + hmac-drbg@1.0.1: + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + hono@4.12.22: {} + + html-url-attributes@3.0.1: {} + + https-browserify@1.0.0: {} + + idb-keyval@6.2.1: {} + + idb-keyval@6.2.4: {} + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + inherits@2.0.4: {} + + inline-style-parser@0.2.7: {} + + iron-webcrypto@1.2.1: {} + + is-alphabetical@1.0.4: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@1.0.4: + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-arrayish@0.2.1: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-buffer@1.1.6: {} + + is-callable@1.2.7: {} + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.3 + + is-decimal@1.0.4: {} + + is-decimal@2.0.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@1.0.4: {} + + is-hexadecimal@2.0.1: {} + + is-nan@1.3.2: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + + is-number@7.0.0: {} + + is-plain-obj@4.1.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + + is-retry-allowed@2.2.0: {} + + is-stream@2.0.1: {} + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isomorphic-timers-promises@1.0.1: {} + + isows@1.0.6(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + + isows@1.0.7(ws@8.18.2(bufferutil@4.1.0)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.18.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) + + isows@1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + + isows@1.0.7(ws@8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + + jiti@1.21.7: {} + + jose@6.2.3: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json-rpc-engine@6.1.0: + dependencies: + '@metamask/safe-event-emitter': 2.0.0 + eth-rpc-errors: 4.0.3 + + json-rpc-random-id@1.0.1: {} + + json5@2.2.3: {} + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + keccak@3.0.4: + dependencies: + node-addon-api: 2.0.2 + node-gyp-build: 4.8.4 + readable-stream: 3.6.2 + + keyvaluestorage-interface@1.0.0: {} + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lit-element@4.2.2: + dependencies: + '@lit-labs/ssr-dom-shim': 1.6.0 + '@lit/reactive-element': 2.1.2 + lit-html: 3.3.3 + + lit-html@3.3.3: + dependencies: + '@types/trusted-types': 2.0.7 + + lit@3.3.0: + dependencies: + '@lit/reactive-element': 2.1.2 + lit-element: 4.2.2 + lit-html: 3.3.3 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.debounce@4.0.8: {} + + lodash@4.18.1: {} + + longest-streak@3.1.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lowlight@1.20.0: + dependencies: + fault: 1.0.4 + highlight.js: 10.7.3 + + lru-cache@11.5.0: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + + math-intrinsics@1.1.0: {} + + md5.js@1.3.5: + dependencies: + hash-base: 3.0.5 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + md5@2.3.0: + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + is-buffer: 1.1.6 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.1 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + + merge2@1.4.1: {} + + micro-ftch@0.3.1: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3(supports-color@5.5.0) + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + miller-rabin@4.0.1: + dependencies: + bn.js: 4.12.3 + brorand: 1.1.0 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + minimalistic-assert@1.0.1: {} + + minimalistic-crypto-utils@1.0.1: {} + + mipd@0.0.7(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + ms@2.1.2: {} + + ms@2.1.3: {} + + msgpackr-extract@3.0.3: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 + optional: true + + msgpackr@1.11.12: + optionalDependencies: + msgpackr-extract: 3.0.3 + + multiformats@9.9.0: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.12: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-addon-api@2.0.2: {} + + node-fetch-native@1.6.7: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.1.2 + optional: true + + node-gyp-build@4.8.4: {} + + node-mock-http@1.0.4: {} + + node-releases@2.0.46: {} + + node-stdlib-browser@1.3.1: + dependencies: + assert: 2.1.0 + browser-resolve: 2.0.0 + browserify-zlib: 0.2.0 + buffer: 5.7.1 + console-browserify: 1.2.0 + constants-browserify: 1.0.0 + create-require: 1.1.1 + crypto-browserify: 3.12.1 + domain-browser: 4.22.0 + events: 3.3.0 + https-browserify: 1.0.0 + isomorphic-timers-promises: 1.0.1 + os-browserify: 0.3.0 + path-browserify: 1.0.1 + pkg-dir: 5.0.0 + process: 0.11.10 + punycode: 1.4.1 + querystring-es3: 0.2.1 + readable-stream: 3.6.2 + stream-browserify: 3.0.0 + stream-http: 3.2.0 + string_decoder: 1.3.0 + timers-browserify: 2.0.12 + tty-browserify: 0.0.1 + url: 0.11.4 + util: 0.12.5 + vm-browserify: 1.1.2 + + normalize-path@3.0.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + obj-multiplex@1.0.0: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + readable-stream: 2.3.8 + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + object-inspect@1.13.4: {} + + object-is@1.1.6: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.4 + + on-exit-leak-free@0.2.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + openapi-fetch@0.13.8: + dependencies: + openapi-typescript-helpers: 0.0.15 + + openapi-typescript-helpers@0.0.15: {} + + os-browserify@0.3.0: {} + + ox@0.14.22(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.6.7(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.6.9(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.4(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.7.1(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.9.17(typescript@5.9.3)(zod@4.4.3): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.4(typescript@5.9.3)(zod@4.4.3) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.9.6(typescript@5.9.3)(zod@3.22.4): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.9.3)(zod@3.22.4) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.9.6(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-try@2.2.0: {} + + pako@1.0.11: {} + + pako@2.1.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-asn1@5.1.9: + dependencies: + asn1.js: 4.10.1 + browserify-aes: 1.2.0 + evp_bytestokey: 1.0.3 + pbkdf2: 3.1.5 + safe-buffer: 5.2.1 + + parse-entities@2.0.0: + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-parse@1.0.7: {} + + path-type@4.0.0: {} + + pbkdf2@3.1.5: + dependencies: + create-hash: 1.2.0 + create-hmac: 1.1.7 + ripemd160: 2.0.3 + safe-buffer: 5.2.1 + sha.js: 2.4.12 + to-buffer: 1.2.2 + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + pify@2.3.0: {} + + pify@3.0.0: {} + + pify@5.0.0: {} + + pino-abstract-transport@0.5.0: + dependencies: + duplexify: 4.1.3 + split2: 4.2.0 + + pino-std-serializers@4.0.0: {} + + pino@7.11.0: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 0.2.0 + pino-abstract-transport: 0.5.0 + pino-std-serializers: 4.0.0 + process-warning: 1.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.1.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 2.8.0 + thread-stream: 0.15.2 + + pirates@4.0.7: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + pkg-dir@5.0.0: + dependencies: + find-up: 5.0.0 + + pngjs@5.0.0: {} + + pony-cause@2.1.11: {} + + popmotion@11.0.3: + dependencies: + framesync: 6.0.1 + hey-listen: 1.0.8 + style-value-types: 5.0.0 + tslib: 2.8.1 + + porto@0.2.35(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)): + dependencies: + '@wagmi/core': 2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + hono: 4.12.22 + idb-keyval: 6.2.4 + mipd: 0.0.7(typescript@5.9.3) + ox: 0.9.17(typescript@5.9.3)(zod@4.4.3) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zod: 4.4.3 + zustand: 5.0.13(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) + optionalDependencies: + '@tanstack/react-query': 5.100.14(react@18.3.1) + react: 18.3.1 + typescript: 5.9.3 + wagmi: 2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + transitivePeerDependencies: + - '@types/react' + - immer + - use-sync-external-store + + poseidon-lite@0.3.0: {} + + possible-typed-array-names@1.1.0: {} + + postcss-import@15.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.12 + + postcss-js@4.1.0(postcss@8.5.15): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.15 + + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.15): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 1.21.7 + postcss: 8.5.15 + + postcss-nested@6.2.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.15: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact@10.24.2: {} + + preact@10.29.2: {} + + prettier-plugin-tailwindcss@0.5.14(prettier@3.8.3): + dependencies: + prettier: 3.8.3 + + prettier@3.8.3: {} + + prismjs@1.27.0: {} + + prismjs@1.30.0: {} + + process-nextick-args@2.0.1: {} + + process-warning@1.0.0: {} + + process@0.11.10: {} + + property-information@5.6.0: + dependencies: + xtend: 4.0.2 + + property-information@7.1.0: {} + + proxy-compare@2.6.0: {} + + proxy-from-env@1.1.0: {} + + proxy-from-env@2.1.0: {} + + public-encrypt@4.0.3: + dependencies: + bn.js: 4.12.3 + browserify-rsa: 4.1.1 + create-hash: 1.2.0 + parse-asn1: 5.1.9 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@1.4.1: {} + + qrcode@1.5.3: + dependencies: + dijkstrajs: 1.0.3 + encode-utf8: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + + qs@6.15.2: + dependencies: + side-channel: 1.1.0 + + query-string@7.1.3: + dependencies: + decode-uri-component: 0.2.2 + filter-obj: 1.1.0 + split-on-first: 1.1.0 + strict-uri-encode: 2.0.0 + + querystring-es3@0.2.1: {} + + queue-microtask@1.2.3: {} + + quick-format-unescaped@4.0.4: {} + + radix3@1.1.2: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + randomfill@1.0.4: + dependencies: + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-is@16.13.1: {} + + react-markdown@9.1.0(@types/react@18.3.29)(react@18.3.1): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 18.3.29 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 18.3.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + react-refresh@0.17.0: {} + + react-router-dom@6.30.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@remix-run/router': 1.23.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 6.30.3(react@18.3.1) + + react-router@6.30.3(react@18.3.1): + dependencies: + '@remix-run/router': 1.23.2 + react: 18.3.1 + + react-syntax-highlighter@15.6.6(react@18.3.1): + dependencies: + '@babel/runtime': 7.29.2 + highlight.js: 10.7.3 + highlightjs-vue: 1.0.0 + lowlight: 1.20.0 + prismjs: 1.30.0 + react: 18.3.1 + refractor: 3.6.0 + + react-transition-state@1.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-use-measure@2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + + readdirp@5.0.0: {} + + real-require@0.1.0: {} + + refractor@3.6.0: + dependencies: + hastscript: 6.0.0 + parse-entities: 2.0.0 + prismjs: 1.27.0 + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.1 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.1: + dependencies: + jsesc: 3.1.0 + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + require-directory@2.1.1: {} + + require-main-filename@2.0.0: {} + + resize-observer-polyfill@1.5.1: {} + + resolve-from@4.0.0: {} + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + ripemd160@2.0.3: + dependencies: + hash-base: 3.1.2 + inherits: 2.0.4 + + rollup@4.60.4: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.4 + '@rollup/rollup-android-arm64': 4.60.4 + '@rollup/rollup-darwin-arm64': 4.60.4 + '@rollup/rollup-darwin-x64': 4.60.4 + '@rollup/rollup-freebsd-arm64': 4.60.4 + '@rollup/rollup-freebsd-x64': 4.60.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 + '@rollup/rollup-linux-arm-musleabihf': 4.60.4 + '@rollup/rollup-linux-arm64-gnu': 4.60.4 + '@rollup/rollup-linux-arm64-musl': 4.60.4 + '@rollup/rollup-linux-loong64-gnu': 4.60.4 + '@rollup/rollup-linux-loong64-musl': 4.60.4 + '@rollup/rollup-linux-ppc64-gnu': 4.60.4 + '@rollup/rollup-linux-ppc64-musl': 4.60.4 + '@rollup/rollup-linux-riscv64-gnu': 4.60.4 + '@rollup/rollup-linux-riscv64-musl': 4.60.4 + '@rollup/rollup-linux-s390x-gnu': 4.60.4 + '@rollup/rollup-linux-x64-gnu': 4.60.4 + '@rollup/rollup-linux-x64-musl': 4.60.4 + '@rollup/rollup-openbsd-x64': 4.60.4 + '@rollup/rollup-openharmony-arm64': 4.60.4 + '@rollup/rollup-win32-arm64-msvc': 4.60.4 + '@rollup/rollup-win32-ia32-msvc': 4.60.4 + '@rollup/rollup-win32-x64-gnu': 4.60.4 + '@rollup/rollup-win32-x64-msvc': 4.60.4 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-stable-stringify@2.5.0: {} + + sax@1.6.0: {} + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@6.3.1: {} + + semver@7.8.1: {} + + set-blocking@2.0.0: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + setimmediate@1.0.5: {} + + sha.js@2.4.12: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + shallowequal@1.1.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + slash@3.0.0: {} + + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3(supports-color@5.5.0) + engine.io-client: 6.6.5(bufferutil@4.1.0)(utf-8-validate@5.0.10) + socket.io-parser: 4.2.6 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.6: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + sonic-boom@2.8.0: + dependencies: + atomic-sleep: 1.0.0 + + source-map-js@1.2.1: {} + + source-map@0.5.7: {} + + space-separated-tokens@1.1.5: {} + + space-separated-tokens@2.0.2: {} + + split-on-first@1.1.0: {} + + split2@4.2.0: {} + + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + + stream-http@3.2.0: + dependencies: + builtin-status-codes: 3.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + xtend: 4.0.2 + + stream-shift@1.0.3: {} + + strict-uri-encode@2.0.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-outer@1.0.1: + dependencies: + escape-string-regexp: 1.0.5 + + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + style-value-types@5.0.0: + dependencies: + hey-listen: 1.0.8 + tslib: 2.8.1 + + styled-components@5.3.11(@babel/core@7.29.0)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1): + dependencies: + '@babel/helper-module-imports': 7.28.6(supports-color@5.5.0) + '@babel/traverse': 7.29.0(supports-color@5.5.0) + '@emotion/is-prop-valid': 1.4.0 + '@emotion/stylis': 0.8.5 + '@emotion/unitless': 0.7.5 + babel-plugin-styled-components: 2.3.0(@babel/core@7.29.0)(styled-components@5.3.11(@babel/core@7.29.0)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1))(supports-color@5.5.0) + css-to-react-native: 3.2.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 16.13.1 + shallowequal: 1.1.0 + supports-color: 5.5.0 + transitivePeerDependencies: + - '@babel/core' + + stylis@4.2.0: {} + + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.16 + ts-interface-checker: 0.1.13 + + superstruct@1.0.4: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svg-parser@2.0.4: {} + + svgo@3.3.3: + dependencies: + commander: 7.2.0 + css-select: 5.2.2 + css-tree: 2.3.1 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.6.0 + + tailwindcss@3.4.19: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.15 + postcss-import: 15.1.0(postcss@8.5.15) + postcss-js: 4.1.0(postcss@8.5.15) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.15) + postcss-nested: 6.2.0(postcss@8.5.15) + postcss-selector-parser: 6.1.2 + resolve: 1.22.12 + sucrase: 3.35.1 + transitivePeerDependencies: + - tsx + - yaml + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + thread-stream@0.15.2: + dependencies: + real-require: 0.1.0 + + timers-browserify@2.0.12: + dependencies: + setimmediate: 1.0.5 + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + to-buffer@1.2.2: + dependencies: + isarray: 2.0.5 + safe-buffer: 5.2.1 + typed-array-buffer: 1.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tr46@0.0.3: {} + + trim-lines@3.0.1: {} + + trim-repeated@1.0.0: + dependencies: + escape-string-regexp: 1.0.5 + + trough@2.2.0: {} + + ts-interface-checker@0.1.13: {} + + tsconfck@3.1.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + tslib@1.14.1: {} + + tslib@2.7.0: {} + + tslib@2.8.1: {} + + tty-browserify@0.0.1: {} + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typescript@5.9.3: {} + + ufo@1.6.4: {} + + uint8arrays@3.1.0: + dependencies: + multiformats: 9.9.0 + + uncrypto@0.1.3: {} + + undici-types@6.19.8: {} + + undici-types@7.25.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + universalify@2.0.1: {} + + unstorage@1.17.5(idb-keyval@6.2.4): + dependencies: + anymatch: 3.1.3 + chokidar: 5.0.0 + destr: 2.0.5 + h3: 1.15.11 + lru-cache: 11.5.0 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.4 + optionalDependencies: + idb-keyval: 6.2.4 + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + url@0.11.4: + dependencies: + punycode: 1.4.1 + qs: 6.15.2 + + use-sync-external-store@1.2.0(react@18.3.1): + dependencies: + react: 18.3.1 + + use-sync-external-store@1.4.0(react@18.3.1): + dependencies: + react: 18.3.1 + + utf-8-validate@5.0.10: + dependencies: + node-gyp-build: 4.8.4 + + util-deprecate@1.0.2: {} + + util@0.12.5: + dependencies: + inherits: 2.0.4 + is-arguments: 1.2.0 + is-generator-function: 1.1.2 + is-typed-array: 1.1.15 + which-typed-array: 1.1.20 + + uuid@10.0.0: {} + + uuid@8.3.2: {} + + uuid@9.0.1: {} + + valtio@1.13.2(@types/react@18.3.29)(react@18.3.1): + dependencies: + derive-valtio: 0.1.0(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1)) + proxy-compare: 2.6.0 + use-sync-external-store: 1.2.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.29 + react: 18.3.1 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + viem@2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): + dependencies: + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) + isows: 1.0.6(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.6.7(typescript@5.9.3)(zod@3.25.76) + ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.30.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) + isows: 1.0.7(ws@8.18.2(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.7.1(typescript@5.9.3)(zod@3.25.76) + ws: 8.18.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.9.3)(zod@3.22.4) + isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.9.6(typescript@5.9.3)(zod@3.22.4) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.9.3)(zod@3.25.76) + isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.9.6(typescript@5.9.3)(zod@3.25.76) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.50.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + isows: 1.0.7(ws@8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.14.22(typescript@5.9.3)(zod@3.25.76) + ws: 8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + vite-plugin-node-polyfills@0.22.0(rollup@4.60.4)(vite@5.4.21(@types/node@22.7.5)): + dependencies: + '@rollup/plugin-inject': 5.0.5(rollup@4.60.4) + node-stdlib-browser: 1.3.1 + vite: 5.4.21(@types/node@22.7.5) + transitivePeerDependencies: + - rollup + + vite-plugin-top-level-await@1.6.0(rollup@4.60.4)(vite@5.4.21(@types/node@22.7.5)): + dependencies: + '@rollup/plugin-virtual': 3.0.2(rollup@4.60.4) + '@swc/core': 1.15.40 + '@swc/wasm': 1.15.40 + uuid: 10.0.0 + vite: 5.4.21(@types/node@22.7.5) + transitivePeerDependencies: + - '@swc/helpers' + - rollup + + vite-tsconfig-paths@4.3.2(typescript@5.9.3)(vite@5.4.21(@types/node@22.7.5)): + dependencies: + debug: 4.4.3(supports-color@5.5.0) + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.9.3) + optionalDependencies: + vite: 5.4.21(@types/node@22.7.5) + transitivePeerDependencies: + - supports-color + - typescript + + vite@5.4.21(@types/node@22.7.5): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.15 + rollup: 4.60.4 + optionalDependencies: + '@types/node': 22.7.5 + fsevents: 2.3.3 + + vm-browserify@1.1.2: {} + + wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76): + dependencies: + '@tanstack/react-query': 5.100.14(react@18.3.1) + '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + react: 18.3.1 + use-sync-external-store: 1.4.0(react@18.3.1) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/query-core' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - expo-auth-session + - expo-crypto + - expo-web-browser + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react-native + - supports-color + - uploadthing + - utf-8-validate + - zod + + webextension-polyfill@0.10.0: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-module@2.0.1: {} + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + ws@7.5.11(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.17.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.18.2(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + xmlhttprequest-ssl@2.1.2: {} + + xtend@4.0.2: {} + + y18n@4.0.3: {} + + yallist@3.1.1: {} + + yaml@1.10.3: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + yocto-queue@0.1.0: {} + + zod@3.22.4: {} + + zod@3.25.76: {} + + zod@4.4.3: {} + + zustand@5.0.0(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)): + optionalDependencies: + '@types/react': 18.3.29 + react: 18.3.1 + use-sync-external-store: 1.4.0(react@18.3.1) + + zustand@5.0.13(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)): + optionalDependencies: + '@types/react': 18.3.29 + react: 18.3.1 + use-sync-external-store: 1.4.0(react@18.3.1) + + zustand@5.0.3(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)): + optionalDependencies: + '@types/react': 18.3.29 + react: 18.3.1 + use-sync-external-store: 1.4.0(react@18.3.1) + + zwitch@2.0.4: {} diff --git a/examples/CRISP/client/src/components/Cards/PollCardResult.tsx b/examples/CRISP/client/src/components/Cards/PollCardResult.tsx index 666ba0cc01..58a4312ca6 100644 --- a/examples/CRISP/client/src/components/Cards/PollCardResult.tsx +++ b/examples/CRISP/client/src/components/Cards/PollCardResult.tsx @@ -20,9 +20,13 @@ type PollCardResultProps = { } const PollCardResult: React.FC = ({ isResult, results, totalVotes, isActive }) => { const validVotes = results.reduce((sum, poll) => sum + Number.parseInt(poll.votes.toString(), 10), 0) + const percentDenominator = validVotes > 0 ? validVotes : totalVotes const calculatePercentage = (votes: number) => { - return ((votes / validVotes) * 100).toFixed(0) + if (percentDenominator <= 0) { + return '0' + } + return ((votes / percentDenominator) * 100).toFixed(0) } return ( diff --git a/examples/CRISP/client/src/providers/Web3Provider.tsx b/examples/CRISP/client/src/providers/Web3Provider.tsx index 3a54781f1e..8b68429931 100644 --- a/examples/CRISP/client/src/providers/Web3Provider.tsx +++ b/examples/CRISP/client/src/providers/Web3Provider.tsx @@ -5,7 +5,6 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import { WagmiProvider, createConfig, http } from 'wagmi' -import { sepolia, anvil } from 'wagmi/chains' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { ConnectKitProvider, getDefaultConfig } from 'connectkit' import React from 'react' @@ -15,14 +14,15 @@ const walletConnectProjectId = import.meta.env.VITE_WALLETCONNECT_PROJECT_ID || if (!walletConnectProjectId) console.warn('VITE_WALLETCONNECT_PROJECT_ID is not set in .env file. WalletConnect will not function properly.') +const chain = getChain() +const rpcUrl = import.meta.env.VITE_RPC_URL || 'http://127.0.0.1:8545' + const config = createConfig( getDefaultConfig({ appName: 'CRISP', - enableFamily: false, - chains: [getChain()], + chains: [chain], transports: { - [anvil.id]: http(anvil.rpcUrls.default.http[0]), - [sepolia.id]: http(sepolia.rpcUrls.default.http[0]), + [chain.id]: http(rpcUrl), }, walletConnectProjectId: walletConnectProjectId, }), diff --git a/examples/CRISP/client/src/utils/methods.ts b/examples/CRISP/client/src/utils/methods.ts index 69e03ee3ad..2af45c2cad 100644 --- a/examples/CRISP/client/src/utils/methods.ts +++ b/examples/CRISP/client/src/utils/methods.ts @@ -23,6 +23,9 @@ export const convertTimestampToDate = (timestamp: number, secondsToAdd: number = } export const getChain = (): Chain => { + const chainId = Number.parseInt(String(import.meta.env.VITE_CHAIN_ID ?? ''), 10) + if (chainId === anvil.id) return anvil + if (chainId === sepolia.id) return sepolia return import.meta.env.DEV ? anvil : sepolia } @@ -50,13 +53,13 @@ export const convertPollData = (request: PollRequestResult[]): PollResult[] => { const options: PollOption[] = [ { value: 0, - votes: poll.tally[0] ?? 0, + votes: Number.parseInt(String(poll.tally[0] ?? '0'), 10) || 0, label: poll.option_1_emoji, checked: false, }, { value: 1, - votes: poll.tally[1] ?? 0, + votes: Number.parseInt(String(poll.tally[1] ?? '0'), 10) || 0, label: poll.option_2_emoji, checked: false, }, diff --git a/examples/CRISP/client/vercel.json b/examples/CRISP/client/vercel.json index e2cbdd5621..229370f343 100644 --- a/examples/CRISP/client/vercel.json +++ b/examples/CRISP/client/vercel.json @@ -1,5 +1,9 @@ { - "installCommand": "pnpm install --no-link-workspace-packages --no-lockfile", + "$schema": "https://openapi.vercel.sh/vercel.json", + "framework": "vite", + "installCommand": "corepack enable && corepack prepare pnpm@10.7.1 --activate && pnpm install --ignore-workspace", + "buildCommand": "pnpm run build", + "outputDirectory": "dist", "headers": [ { "source": "/(.*)", diff --git a/examples/CRISP/crisp.dev.env.example b/examples/CRISP/crisp.dev.env.example new file mode 100644 index 0000000000..aa1923a39a --- /dev/null +++ b/examples/CRISP/crisp.dev.env.example @@ -0,0 +1,17 @@ +# CRISP local development profile (copied to crisp.dev.env on first `pnpm dev:setup`). +# Edit crisp.dev.env, then re-run `pnpm dev:setup` (and `pnpm dev:up` for a fresh deploy). +# +# See docs/PROOF_AGGREGATION_AND_ZK.md for details. + +# BFV parameter preset for DKG / on-chain Honk verifiers. +# Must match Enclave param_set used at requestE3 time (server uses param_set 0 today). +# insecure-512 -> param_set 0 (Micro committee, fast local dev) +# secure-8192 -> param_set 1 (production-grade; slower proving) +CRISP_BFV_PRESET=insecure-512 + +# When true: +# - dev:setup runs `pnpm build:circuits --preset ` +# - dev:up deploy sets ENABLE_ZK_VERIFICATION=true (BfvPkVerifier, fold attestations) +# - server/.env gets E3_PROOF_AGGREGATION_ENABLED=true +# When false (default): mock verifiers, no DKG circuit build, faster DKG. +CRISP_PROOF_AGGREGATION_ENABLED=false diff --git a/examples/CRISP/docs/PROOF_AGGREGATION_AND_ZK.md b/examples/CRISP/docs/PROOF_AGGREGATION_AND_ZK.md new file mode 100644 index 0000000000..6eadd90e33 --- /dev/null +++ b/examples/CRISP/docs/PROOF_AGGREGATION_AND_ZK.md @@ -0,0 +1,180 @@ +# CRISP: proof aggregation and on-chain ZK verification + +## Configuration (`crisp.dev.env`) + +**Source of truth for local dev:** `examples/CRISP/crisp.dev.env` (from `crisp.dev.env.example`). + +| Variable | Default | Effect | +| --------------------------------- | -------------- | ----------------------------------------------------- | +| `CRISP_BFV_PRESET` | `insecure-512` | `pnpm build:circuits --preset` when aggregation is on | +| `CRISP_PROOF_AGGREGATION_ENABLED` | `false` | Drives setup, deploy, and `server/.env` | + +`pnpm dev:setup` copies the example if missing, syncs `E3_PROOF_AGGREGATION_ENABLED` into +`server/.env`, and builds DKG circuits only when aggregation is `true`. `pnpm dev:up` → +`crisp_deploy.sh` sets `ENABLE_ZK_VERIFICATION` from the same file. + +After changing `crisp.dev.env`, re-run `pnpm dev:setup` and a fresh `pnpm dev:up` (wipe +`.enclave/data` when switching modes). + +Lower-level switches (kept in sync by the scripts): + +| Switch | Where | Effect | +| ------------------------------ | ---------------------------------------------------- | ----------------------------- | +| `E3_PROOF_AGGREGATION_ENABLED` | `server/.env` (managed by setup) | Passed to `Enclave.requestE3` | +| `ENABLE_ZK_VERIFICATION` | Set at deploy from `CRISP_PROOF_AGGREGATION_ENABLED` | Real vs mock BFV verifiers | + +Misalignment causes `publishCommittee` to revert with **`VkHashMismatch()`** (`0x0c260259`). + +--- + +## Mode A — Local dev without proof aggregation (recommended) + +Use this for day-to-day CRISP development: faster DKG, no recursive proving, no on-chain BFV +verifier checks. + +### Configuration + +```bash +# crisp.dev.env +CRISP_BFV_PRESET=insecure-512 +CRISP_PROOF_AGGREGATION_ENABLED=false +``` + +### Steps + +```bash +# From examples/CRISP +pnpm dev:setup # once — skips DKG circuit build, syncs server/.env +pnpm dev:up +``` + +After deploy, ensure `server/.env` and `client/.env` match addresses printed by deploy or +`packages/crisp-contracts/deployed_contracts.json` → `localhost` (see +[Address sync](#address-sync-after-deploy)). + +```bash +pnpm cli init +``` + +### What you should see + +- Ciphernodes skip long `NodeDkgFold` / `zk_dkg_aggregation` runs +- `publishCommittee` succeeds without a DKG Honk proof (empty `proof` bytes are allowed when + aggregation is disabled on the E3) +- `POST /rounds/current` returns 200 once the indexer has recorded the round + +--- + +## Mode B — Full proof aggregation + on-chain ZK verification + +Use this to exercise the production DKG path: recursive folds, fold attestations, DKG aggregator +Honk proof, and `BfvPkVerifier` checks at `publishCommittee`. + +### Configuration + +```bash +# crisp.dev.env +CRISP_BFV_PRESET=insecure-512 +CRISP_PROOF_AGGREGATION_ENABLED=true +``` + +CRISP `requestE3` still uses on-chain `param_set = 0` (`InsecureThreshold512`) unless you change the +server — keep `CRISP_BFV_PRESET=insecure-512` for the default Micro committee. + +### Steps + +```bash +cd examples/CRISP +# Edit crisp.dev.env (or crisp.dev.env.example → crisp.dev.env) as above +pnpm dev:setup # builds DKG circuits + syncs server/.env +rm -rf .enclave/data # required when switching from Mode A +pnpm dev:up # deploy with ENABLE_ZK_VERIFICATION=true +pnpm cli init +``` + +`dev:setup` runs `pnpm build:circuits --preset ` before contract compile. `dev:up` +deploys via `crisp_deploy.sh` with `ENABLE_ZK_VERIFICATION=true`. + +**Do not** run `pnpm build:circuits` with a different preset after deploy without redeploying — that +causes **`VkHashMismatch()`** at `publishCommittee`. + +Expect DKG aggregation to take on the order of **minutes** per committee (fold + aggregator +proving). + +### What you should see + +- Logs: `loaded dkgFoldAttestationVerifier`, `NodeDkgFold complete`, `zk_dkg_aggregation`, then + `Publishing PublicKeyAggregated (dkg_evm_proof=present)` +- On-chain: `publishCommittee` succeeds (no `VkHashMismatch`) +- Registry / Enclave transition to key published; CRISP indexer can serve `/rounds/current` + +--- + +## Invalid combinations + +| Deploy | `E3_PROOF_AGGREGATION_ENABLED` | Result | +| ---------------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Mock (`ENABLE_ZK_VERIFICATION` unset) | `true` | Ciphernodes generate real aggregation proofs, but `Enclave.pkVerifier` is `MockPkVerifier`. Attestation path may still run if a previous ZK deploy left `DkgFoldAttestationVerifier` on the registry from stale `deployed_contracts.json` on a **fresh** Anvil — always use `clean:deployments` + fresh chain. Prefer wiping `.enclave/data` and setting aggregation `false` for mock deploy. | +| ZK (`ENABLE_ZK_VERIFICATION=true`) | `false` | Valid but skips on-chain DKG proof verification; committee publication uses empty proof bytes. | +| ZK, circuits recompiled **after** deploy | `true` | **`VkHashMismatch()`** at `publishCommittee` — redeploy `BfvPkVerifier` (full ZK deploy) after `pnpm compile:circuits`. | + +--- + +## Address sync after deploy + +`pnpm dev:up` runs deploy then automatically updates: + +- `enclave.config.yaml` (ciphernode contract watches) +- `server/.env` (`ENCLAVE_ADDRESS`, `E3_PROGRAM_ADDRESS`, `CRISP_VOTING_TOKEN`, registry, fee token, + mock refs, `E3_PROOF_AGGREGATION_ENABLED` from `crisp.dev.env`) +- `client/.env` (`VITE_CRISP_TOKEN`) + +No manual copy from `deployed_contracts.json` is required. Stale addresses only happen if you skip +`dev:up` deploy and reuse an old Anvil state with new `.env` files. + +--- + +## Troubleshooting + +### `publishCommittee` reverts — `0x0c260259` (`VkHashMismatch`) + +**Cause:** `BfvPkVerifier` immutables (`expectedNodesFoldKeyHash`, `expectedC5KeyHash`) do not match +the VK hashes embedded in the DKG aggregator proof (usually circuits were rebuilt after verifier +deploy). + +**Fix:** + +1. Set `CRISP_PROOF_AGGREGATION_ENABLED=true` in `crisp.dev.env` (and matching `CRISP_BFV_PRESET`) +2. `pnpm dev:setup` then `rm -rf .enclave/data && pnpm dev:up` +3. `pnpm cli init` + +### `POST /rounds/current` → 500 + +Often a **symptom**, not the root cause: the CRISP indexer has no current round until on-chain DKG +progresses (e.g. committee key published). Fix DKG / `publishCommittee` first, then retry. If the +round was never created, run `pnpm cli init` after the server and ciphernodes are healthy. + +### `Historical events channel closed before all chains reported` + +Expected on localhost if Sepolia (`11155111`) is configured in ciphernode EVM sync but no Sepolia +RPC is running. Harmless for CRISP-on-Anvil. + +### After changing mode + +1. Fresh deploy (`clean:deployments` + deploy script for chosen mode) +2. Sync `.env` / `enclave.config.yaml` +3. `rm -rf .enclave/data` +4. Restart stack + `pnpm cli init` + +--- + +## Reference: what the scripts do + +| Step | Mode A (`CRISP_PROOF_AGGREGATION_ENABLED=false`) | Mode B (`=true`) | +| ---------------- | ----------------------------------------------------------------- | --------------------------------------------------------- | +| `pnpm dev:setup` | Skips `build:circuits`; sets aggregation `false` in `server/.env` | `pnpm build:circuits --preset …`; sets aggregation `true` | +| `pnpm dev:up` | Mock BFV verifiers | `ENABLE_ZK_VERIFICATION=true` + prints env vars | + +See also: `packages/enclave-contracts/scripts/deployEnclave.ts`, +`packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol`, and +`agent/flow-trace/04_DKG_AND_COMPUTATION.md` for the full DKG publication flow. diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index c6f2dc79f1..ed52e89d59 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -1,10 +1,12 @@ +# localhost: sync Enclave stack from packages/enclave-contracts/deployed_contracts.json after pnpm evm:deploy. +# e3_program (CRISPProgram): updated by `USE_MOCKS=true pnpm deploy:contracts` in packages/crisp-contracts. chains: - name: localhost rpc_url: ws://localhost:8545 contracts: e3_program: - address: "0x9d4454B023096f34B160D6B654540c56A1F81688" - deploy_block: 27 + address: "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0" + deploy_block: 38 enclave: address: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" deploy_block: 14 diff --git a/examples/CRISP/package.json b/examples/CRISP/package.json index 309e70ee51..b1916a736d 100644 --- a/examples/CRISP/package.json +++ b/examples/CRISP/package.json @@ -20,6 +20,9 @@ "ciphernode:mint:tokens": "pnpm -C packages/crisp-contracts ciphernode:mint:tokens", "ciphernode:add:self": "pnpm -C packages/crisp-contracts ciphernode:add:self", "deploy:contracts": "pnpm -C packages/crisp-contracts deploy:contracts", + "deploy:contracts:mock": "pnpm -C packages/crisp-contracts deploy:contracts:mock", + "deploy:contracts:full": "pnpm -C packages/crisp-contracts deploy:contracts:full", + "deploy:contracts:full:mock": "pnpm -C packages/crisp-contracts deploy:contracts:full:mock", "test": "pnpm test:e2e", "test:circuits": "cd circuits/lib && nargo test", "compile:circuits": "bash ./scripts/compile_circuits.sh", diff --git a/examples/CRISP/packages/crisp-contracts/.env.example b/examples/CRISP/packages/crisp-contracts/.env.example index 6279b65f72..1fd7fadc1b 100644 --- a/examples/CRISP/packages/crisp-contracts/.env.example +++ b/examples/CRISP/packages/crisp-contracts/.env.example @@ -6,3 +6,6 @@ PRIVATE_KEY="" RPC_URL="" # Whether to use mock verifier and deploy a new mock token contract USE_MOCKS= + +# Prefer editing ../../crisp.dev.env — dev:setup / crisp_deploy.sh set ENABLE_ZK_VERIFICATION +# from CRISP_PROOF_AGGREGATION_ENABLED. See ../../docs/PROOF_AGGREGATION_AND_ZK.md. diff --git a/examples/CRISP/packages/crisp-contracts/README.md b/examples/CRISP/packages/crisp-contracts/README.md index 5868660933..e32c4616fc 100644 --- a/examples/CRISP/packages/crisp-contracts/README.md +++ b/examples/CRISP/packages/crisp-contracts/README.md @@ -22,30 +22,18 @@ pnpm test ## Deployment -### For testing +Local deploy is driven by **`../../crisp.dev.env`** (see +**[../../docs/PROOF_AGGREGATION_AND_ZK.md](../../docs/PROOF_AGGREGATION_AND_ZK.md)**): -For testing, you can deploy the contracts without using the Risc0 verifier. The following command -can be run: +- `pnpm dev:setup` — applies profile, builds DKG circuits when + `CRISP_PROOF_AGGREGATION_ENABLED=true` +- `pnpm dev:up` → `scripts/crisp_deploy.sh` — sets `ENABLE_ZK_VERIFICATION` from the same file -```bash -pnpm deploy:contracts:full:mock -``` - -This will also print out the environment variables needed for the CRISP server to work with your -newly deployed contracts. - -### Full deployment with Risc0Verifier - -You can deploy CRISP contracts only using: - -```bash -pnpm deploy:contracts -``` - -Or the following to deploy Enclave contracts too (useful for testing scenarios): +### CRISP-only deploy (Enclave already deployed) ```bash -pnpm deploy:contracts:full +pnpm deploy:contracts # production RISC0 verifier +pnpm deploy:contracts:full # also deploy Enclave stack (no ZK unless ENABLE_ZK_VERIFICATION=true) ``` ## CRISP Program diff --git a/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts b/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts index 57f0711fd5..9a1fd34e34 100644 --- a/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts +++ b/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts @@ -3,8 +3,9 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -import { deployEnclave, readDeploymentArgs, updateE3Config } from '@enclave-e3/contracts/scripts' +import { deployEnclave, updateE3Config } from '@enclave-e3/contracts/scripts' import { deployCRISPContracts } from './crisp' +import { syncCrispEnvFromDeployments } from './syncCrispEnv' import path from 'path' import hre from 'hardhat' @@ -28,37 +29,20 @@ const __dirname = path.dirname(__filename) * Deploys the Enclave and CRISP contracts */ export const deploy = async () => { - const chain = hre.globalOptions.network + const chain = hre.globalOptions.network ?? 'localhost' const shouldDeployEnclave = Boolean(process.env.DEPLOY_ENCLAVE) - const shouldPrintEnv = Boolean(process.env.PRINT_ENV_VARS) + const withZkVerification = process.env.ENABLE_ZK_VERIFICATION === 'true' - // Mock BFV verifiers only: CRISP E2E uses E3_PROOF_AGGREGATION_ENABLED=false and does not - // ship compiled `*.vk_recursive_hash` artifacts required by BfvPkVerifier / BfvDecryptionVerifier. if (shouldDeployEnclave) { - await deployEnclave(true, false) + await deployEnclave(true, withZkVerification) } await deployCRISPContracts() - // this expects you to run it from CRISP's root - updateE3Config(chain, path.join(__dirname, '..', '..', '..', 'enclave.config.yaml'), contractMapping) + const enclaveConfigPath = path.join(__dirname, '..', '..', '..', 'enclave.config.yaml') + updateE3Config(chain, enclaveConfigPath, contractMapping) - if (shouldPrintEnv) { - const enclaveAddress = readDeploymentArgs('Enclave', chain)?.address - const tokenAddress = readDeploymentArgs('MockUSDC', chain)?.address - const programAddress = readDeploymentArgs('CRISPProgram', chain)?.address - const ciphernodeRegistryAddress = readDeploymentArgs('CiphernodeRegistryOwnable', chain)?.address - - if (!enclaveAddress || !tokenAddress || !programAddress || !ciphernodeRegistryAddress) { - console.error('Error: Missing deployment addresses. Ensure all contracts are deployed.') - return - } - - console.log('\nAdd these to your server .env') - console.log( - `ENCLAVE_ADDRESS=${enclaveAddress}\nFEE_TOKEN_ADDRESS=${tokenAddress}\nE3_PROGRAM_ADDRESS=${programAddress}\nCIPHERNODE_REGISTRY_ADDRESS=${ciphernodeRegistryAddress}`, - ) - } + syncCrispEnvFromDeployments(chain) } deploy().catch((err) => { diff --git a/examples/CRISP/packages/crisp-contracts/deploy/syncCrispEnv.ts b/examples/CRISP/packages/crisp-contracts/deploy/syncCrispEnv.ts new file mode 100644 index 0000000000..57b2685e72 --- /dev/null +++ b/examples/CRISP/packages/crisp-contracts/deploy/syncCrispEnv.ts @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +import fs from 'fs' +import path from 'path' +import { fileURLToPath } from 'url' + +import { readDeploymentArgs } from '@enclave-e3/contracts/scripts' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + +/** examples/CRISP */ +const CRISP_ROOT = path.join(__dirname, '..', '..', '..') + +function parseSimpleEnvFile(filePath: string): Record { + if (!fs.existsSync(filePath)) { + return {} + } + const out: Record = {} + for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) { + const trimmed = line.trim() + if (!trimmed || trimmed.startsWith('#')) { + continue + } + const eq = trimmed.indexOf('=') + if (eq === -1) { + continue + } + out[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim() + } + return out +} + +function ensureEnvFile(envPath: string, examplePath: string): void { + if (!fs.existsSync(envPath)) { + if (!fs.existsSync(examplePath)) { + throw new Error(`Missing ${examplePath}; cannot create ${envPath}`) + } + fs.copyFileSync(examplePath, envPath) + } +} + +/** Set or append KEY=value lines; preserves comments and unrelated keys. */ +function applyEnvUpdates(envPath: string, updates: Record): void { + let content = fs.readFileSync(envPath, 'utf8') + for (const [key, value] of Object.entries(updates)) { + const pattern = new RegExp(`^${key}=.*$`, 'm') + const line = `${key}=${value}` + if (pattern.test(content)) { + content = content.replace(pattern, line) + } else { + if (!content.endsWith('\n')) { + content += '\n' + } + content += `${line}\n` + } + } + fs.writeFileSync(envPath, content) +} + +function deploymentAddress(contractName: string, chain: string): string | undefined { + return readDeploymentArgs(contractName, chain)?.address +} + +/** + * Writes localhost deployment addresses into server/.env and client/.env, and + * syncs E3_PROOF_AGGREGATION_ENABLED from crisp.dev.env. + */ +export function syncCrispEnvFromDeployments(chain: string): void { + const enclaveAddress = deploymentAddress('Enclave', chain) + const feeTokenAddress = deploymentAddress('MockUSDC', chain) + const programAddress = deploymentAddress('CRISPProgram', chain) + const registryAddress = deploymentAddress('CiphernodeRegistryOwnable', chain) + const votingTokenAddress = deploymentAddress('MockVotingToken', chain) + + const missing: string[] = [] + if (!enclaveAddress) missing.push('Enclave') + if (!feeTokenAddress) missing.push('MockUSDC') + if (!programAddress) missing.push('CRISPProgram') + if (!registryAddress) missing.push('CiphernodeRegistryOwnable') + if (!votingTokenAddress) missing.push('MockVotingToken') + + if (missing.length > 0) { + throw new Error(`Cannot sync CRISP .env files: missing deployments for ${missing.join(', ')} on chain "${chain}"`) + } + + const crispDev = parseSimpleEnvFile(path.join(CRISP_ROOT, 'crisp.dev.env')) + const proofAggregation = + crispDev.CRISP_PROOF_AGGREGATION_ENABLED ?? + parseSimpleEnvFile(path.join(CRISP_ROOT, 'crisp.dev.env.example')).CRISP_PROOF_AGGREGATION_ENABLED ?? + 'false' + + const serverEnv = path.join(CRISP_ROOT, 'server', '.env') + const clientEnv = path.join(CRISP_ROOT, 'client', '.env') + + ensureEnvFile(serverEnv, path.join(CRISP_ROOT, 'server', '.env.example')) + ensureEnvFile(clientEnv, path.join(CRISP_ROOT, 'client', '.env.example')) + + const serverUpdates: Record = { + ENCLAVE_ADDRESS: enclaveAddress!, + FEE_TOKEN_ADDRESS: feeTokenAddress!, + E3_PROGRAM_ADDRESS: programAddress!, + CIPHERNODE_REGISTRY_ADDRESS: registryAddress!, + CRISP_VOTING_TOKEN: votingTokenAddress!, + E3_PROOF_AGGREGATION_ENABLED: proofAggregation, + } + + const mockMappings: Array<[string, string]> = [ + ['MOCK_COMPUTE_PROVIDER_ADDRESS', 'MockComputeProvider'], + ['MOCK_DECRYPTION_VERIFIER_ADDRESS', 'MockDecryptionVerifier'], + ['MOCK_PK_VERIFIER_ADDRESS', 'MockPkVerifier'], + ['MOCK_E3_PROGRAM_ADDRESS', 'MockE3Program'], + ] + for (const [envKey, contractName] of mockMappings) { + const addr = deploymentAddress(contractName, chain) + if (addr) { + serverUpdates[envKey] = addr + } + } + + applyEnvUpdates(serverEnv, serverUpdates) + applyEnvUpdates(clientEnv, { + VITE_CRISP_TOKEN: votingTokenAddress!, + }) + + console.log(`Synced deployment addresses → ${path.relative(CRISP_ROOT, serverEnv)}`) + console.log(`Synced VITE_CRISP_TOKEN → ${path.relative(CRISP_ROOT, clientEnv)}`) + console.log(` E3_PROOF_AGGREGATION_ENABLED=${proofAggregation} (from crisp.dev.env)`) +} diff --git a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json index d066ec2941..f4bd486ddb 100644 --- a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json +++ b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json @@ -234,7 +234,7 @@ "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "proxyAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", "proxyAdminAddress": "0x1F708C24a0D3A740cD47cC0444E9480899f3dA7D", - "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" + "implementationAddress": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, "blockNumber": 14, "address": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" @@ -256,42 +256,42 @@ "address": "0x9A676e781A523b5d0C0e43731313A708CB607508" }, "MockComputeProvider": { - "blockNumber": 19, - "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" + "blockNumber": 30, + "address": "0x5eb3Bc0a489C5A8288765d2336659EbCA68FCd00" }, "MockDecryptionVerifier": { - "blockNumber": 20, - "address": "0x851356ae760d987E095750cCeb3bC6014560891C" + "blockNumber": 31, + "address": "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570" }, "MockPkVerifier": { - "blockNumber": 21, - "address": "0xf5059a5D33d5853360D16C683c16e67980206f36" + "blockNumber": 32, + "address": "0x809d550fca64d94Bd9F66E60752A544199cfAC3D" }, "MockE3Program": { - "blockNumber": 22, - "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778" + "blockNumber": 33, + "address": "0x4c5859f0F772848b2D91F1D83E2Fe57935348029" }, "MockRISC0Verifier": { - "address": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf", - "blockNumber": 26 + "address": "0xCD8a1C3ba11CF5ECfa6267617243239504a98d90", + "blockNumber": 37 }, "HonkVerifier": { - "address": "0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf", - "blockNumber": 27 + "address": "0x2bdCC0de6bE1f7D2ee689a0342D76F52E8EFABa3", + "blockNumber": 38 }, "CRISPProgram": { - "address": "0x9d4454B023096f34B160D6B654540c56A1F81688", - "blockNumber": 27, + "address": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + "blockNumber": 38, "constructorArgs": { "enclave": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", - "verifierAddress": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf", - "honkVerifierAddress": "0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf", + "verifierAddress": "0xCD8a1C3ba11CF5ECfa6267617243239504a98d90", + "honkVerifierAddress": "0x2bdCC0de6bE1f7D2ee689a0342D76F52E8EFABa3", "imageId": "0x23734b77b0f76e85623a88d7a82f24c34c94834f2501964ea123b7a2027013a2" } }, "MockVotingToken": { - "address": "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570", - "blockNumber": 29 + "address": "0xc351628EB244ec633d5f21fBD6621e1a683B1181", + "blockNumber": 40 } } } \ No newline at end of file diff --git a/examples/CRISP/scripts/crisp_deploy.sh b/examples/CRISP/scripts/crisp_deploy.sh index 4022484067..72474c41bc 100755 --- a/examples/CRISP/scripts/crisp_deploy.sh +++ b/examples/CRISP/scripts/crisp_deploy.sh @@ -2,11 +2,31 @@ set -euo pipefail -echo "Deploying CRISP Contracts..." +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "${SCRIPT_DIR}/lib/dev_config.sh" + +load_crisp_dev_config +print_crisp_dev_config_summary + +echo "Deploying CRISP contracts..." export PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" +export USE_MOCKS=true +export DEPLOY_ENCLAVE=true -cd packages/crisp-contracts +cd "${CRISP_ROOT}/packages/crisp-contracts" pnpm clean:deployments --network localhost -USE_MOCKS=true pnpm deploy:contracts:full:mock --network localhost + +if [[ "$CRISP_PROOF_AGGREGATION_ENABLED" == "true" ]]; then + export ENABLE_ZK_VERIFICATION=true + echo "Deploy: ENABLE_ZK_VERIFICATION=true (BfvPkVerifier + fold attestation)" +else + unset ENABLE_ZK_VERIFICATION + echo "Deploy: mock BFV verifiers (ENABLE_ZK_VERIFICATION unset)" +fi + +pnpm deploy:contracts --network localhost + +apply_crisp_dev_config_to_server_env diff --git a/examples/CRISP/scripts/lib/dev_config.sh b/examples/CRISP/scripts/lib/dev_config.sh new file mode 100644 index 0000000000..6b6411489f --- /dev/null +++ b/examples/CRISP/scripts/lib/dev_config.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# Shared CRISP local dev configuration. Source from setup.sh / crisp_deploy.sh. + +_crisp_dev_config_root() { + (cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +} + +load_crisp_dev_config() { + CRISP_ROOT="$(_crisp_dev_config_root)" + REPO_ROOT="$(cd "${CRISP_ROOT}/../.." && pwd)" + + local cfg="${CRISP_ROOT}/crisp.dev.env" + if [[ ! -f "$cfg" ]]; then + cp "${CRISP_ROOT}/crisp.dev.env.example" "$cfg" + echo "Created ${cfg} from crisp.dev.env.example" + fi + + set -a + # shellcheck disable=SC1090 + source "$cfg" + set +a + + CRISP_BFV_PRESET="${CRISP_BFV_PRESET:-insecure-512}" + CRISP_PROOF_AGGREGATION_ENABLED="${CRISP_PROOF_AGGREGATION_ENABLED:-false}" + + case "$CRISP_BFV_PRESET" in + insecure-512 | secure-8192) ;; + *) + echo "Invalid CRISP_BFV_PRESET='${CRISP_BFV_PRESET}' (use insecure-512 or secure-8192)" >&2 + exit 1 + ;; + esac + + case "$CRISP_PROOF_AGGREGATION_ENABLED" in + true | false) ;; + *) + echo "Invalid CRISP_PROOF_AGGREGATION_ENABLED='${CRISP_PROOF_AGGREGATION_ENABLED}' (use true or false)" >&2 + exit 1 + ;; + esac + + if [[ "$CRISP_PROOF_AGGREGATION_ENABLED" == "true" ]]; then + export ENABLE_ZK_VERIFICATION=true + else + unset ENABLE_ZK_VERIFICATION + fi + + export CRISP_BFV_PRESET CRISP_PROOF_AGGREGATION_ENABLED CRISP_ROOT REPO_ROOT +} + +_set_env_kv() { + local file=$1 key=$2 value=$3 + if [[ -f "$file" ]] && grep -q "^${key}=" "$file"; then + local tmp + tmp="$(mktemp)" + sed "s|^${key}=.*|${key}=${value}|" "$file" >"$tmp" + mv "$tmp" "$file" + else + echo "${key}=${value}" >>"$file" + fi +} + +apply_crisp_dev_config_to_server_env() { + local server_env="${CRISP_ROOT}/server/.env" + if [[ ! -f "$server_env" ]]; then + cp "${CRISP_ROOT}/server/.env.example" "$server_env" + fi + _set_env_kv "$server_env" "E3_PROOF_AGGREGATION_ENABLED" "$CRISP_PROOF_AGGREGATION_ENABLED" +} + +compile_enclave_dkg_circuits_if_needed() { + if [[ "$CRISP_PROOF_AGGREGATION_ENABLED" != "true" ]]; then + echo "Skipping enclave DKG circuit build (CRISP_PROOF_AGGREGATION_ENABLED=false in crisp.dev.env)" + return 0 + fi + + echo "Building enclave DKG circuits (preset=${CRISP_BFV_PRESET})..." + ( + cd "${REPO_ROOT}" && + pnpm build:circuits --preset "${CRISP_BFV_PRESET}" --skip-if-built + ) +} + +print_crisp_dev_config_summary() { + cat <, } +/// Seconds between `block.timestamp` and `inputWindow[0]` (covers approve + enable txs on Anvil). +const INPUT_WINDOW_START_BUFFER_SECS: u64 = 60; + +const ZERO_ADDRESS: &str = "0x0000000000000000000000000000000000000000"; + +/// `InsufficientCiphernodes(uint256,uint256)` on CiphernodeRegistry. +const INSUFFICIENT_CIPHERNODES_SELECTOR: &str = "0x44ec930f"; + +fn format_request_e3_revert(err: impl std::fmt::Display) -> anyhow::Error { + let msg = err.to_string(); + if msg.contains(INSUFFICIENT_CIPHERNODES_SELECTOR) { + return anyhow!( + "request_e3 reverted: InsufficientCiphernodes — the committee size needs N active \ + operators (Micro N=3) but bondingRegistry.numActiveOperators() is 0. Register \ + ciphernodes before init: run full `pnpm dev:up`, or from examples/CRISP run \ + `pnpm ciphernode:add --ciphernode-address --network localhost` for at least \ + three addresses in enclave.config.yaml (cn1–cn3)." + ); + } + anyhow!( + "request_e3 reverted: {msg}. Common causes: stale E3_PROGRAM_ADDRESS in server/.env \ + (must match deployed CRISPProgram), inputWindow start in the past, or no registered \ + ciphernodes on the chain." + ) +} + +pub fn default_voting_token_hint() -> String { + deployments::localhost_mock_voting_token() + .ok() + .flatten() + .unwrap_or_else(|| ZERO_ADDRESS.to_string()) +} + +fn resolve_voting_token( + token_address: &str, +) -> Result> { + let trimmed = token_address.trim(); + if !trimmed.is_empty() && !trimmed.eq_ignore_ascii_case(ZERO_ADDRESS) { + return Ok(trimmed.to_string()); + } + if let Some(ref configured) = CONFIG.crisp_voting_token { + let configured = configured.trim(); + if !configured.is_empty() && !configured.eq_ignore_ascii_case(ZERO_ADDRESS) { + return Ok(configured.to_string()); + } + } + if let Some(addr) = deployments::localhost_mock_voting_token()? { + info!("Using MockVotingToken from deployed_contracts.json: {addr}"); + return Ok(addr); + } + Err(anyhow!( + "Voting token address is unset. After `pnpm dev:up`, copy `CRISP_VOTING_TOKEN` from deploy \ + output into server/.env, or pass `--token-address `." + ) + .into()) +} + +async fn ensure_e3_program_deployed( + e3_program: Address, +) -> Result<(), Box> { + if let Some(deployed) = deployments::localhost_crisp_program()? { + let deployed_addr: Address = deployed.parse()?; + if deployed_addr != e3_program { + return Err(anyhow!( + "E3_PROGRAM_ADDRESS in server/.env ({e3_program}) does not match deployed \ + CRISPProgram ({deployed_addr}). Re-run `pnpm dev:up` and update server/.env from \ + deploy output (PRINT_ENV_VARS=true)." + ) + .into()); + } + } + + let provider = ProviderBuilder::new().connect(&CONFIG.http_rpc_url).await?; + let code = provider.get_code_at(e3_program).await?; + if code.is_empty() { + return Err(anyhow!( + "No contract bytecode at E3_PROGRAM_ADDRESS {e3_program}. Stale server/.env after \ + redeploy is the usual cause — sync from packages/crisp-contracts/deployed_contracts.json." + ) + .into()); + } + Ok(()) +} + pub async fn get_current_timestamp() -> Result> { let provider = ProviderBuilder::new().connect(&CONFIG.http_rpc_url).await?; let block = provider @@ -79,11 +165,6 @@ pub async fn initialize_crisp_round( token_address: &str, balance_threshold: &str, ) -> Result> { - info!( - "Starting new CRISP round with token address: {} and balance threshold: {}", - token_address, balance_threshold - ); - let contract = EnclaveContract::new( &CONFIG.http_rpc_url, &CONFIG.private_key, @@ -91,6 +172,7 @@ pub async fn initialize_crisp_round( ) .await?; let e3_program: Address = CONFIG.e3_program_address.parse()?; + ensure_e3_program_deployed(e3_program).await?; info!("Enabling E3 Program with address: {}", e3_program); match contract.is_e3_program_enabled(e3_program).await { @@ -109,7 +191,14 @@ pub async fn initialize_crisp_round( Err(e) => info!("Error checking E3 Program enabled: {:?}", e), } - let token_address: Address = token_address.parse()?; + let token_address_str = resolve_voting_token(token_address)?; + + info!( + "Starting new CRISP round with token address: {} and balance threshold: {}", + token_address_str, balance_threshold + ); + + let token_address: Address = token_address_str.parse()?; let balance_threshold = U256::from_str_radix(&balance_threshold, 10)?; // We default to two options for the main CRISP app let num_options = U256::from(2); @@ -156,7 +245,7 @@ pub async fn initialize_crisp_round( current_timestamp ); // Buffer so tx can mine before window opens; end = start + duration so voting window equals e3_duration - let window_start = current_timestamp + 20; + let window_start = current_timestamp + INPUT_WINDOW_START_BUFFER_SECS; let input_window: [U256; 2] = [ U256::from(window_start), U256::from(window_start + CONFIG.e3_duration), @@ -204,12 +293,19 @@ pub async fn initialize_crisp_round( // since there are multiple steps (fee quote, token approval) that could take time. let current_timestamp = get_current_timestamp().await?; // Buffer so tx can mine before window opens; end = start + duration so voting window equals e3_duration - let window_start = current_timestamp + 20; + let window_start = current_timestamp + INPUT_WINDOW_START_BUFFER_SECS; let input_window: [U256; 2] = [ U256::from(window_start), U256::from(window_start + CONFIG.e3_duration), ]; + info!( + "Requesting E3 with input_window [{}, {}] (buffer {}s)", + window_start, + window_start + CONFIG.e3_duration, + INPUT_WINDOW_START_BUFFER_SECS + ); + let (res, e3_id) = contract .request_e3( committee_size, @@ -220,7 +316,8 @@ pub async fn initialize_crisp_round( custom_params_bytes, proof_aggregation_enabled, ) - .await?; + .await + .map_err(format_request_e3_revert)?; info!("E3 request sent. TxHash: {:?}", res.transaction_hash); let e3_id_u64 = u64::try_from(e3_id)?; info!("E3 ID: {}", e3_id_u64); @@ -235,7 +332,10 @@ pub async fn participate_in_existing_round( .with_prompt("Enter CRISP round ID.") .interact_text()?; - let url = format!("{}/rounds/public-key", CONFIG.enclave_server_url); + let url = format!( + "{}/rounds/public-key", + CONFIG.enclave_server_url_for_clients() + ); let resp = client .post(&url) .json(&PKRequest { diff --git a/examples/CRISP/server/src/cli/main.rs b/examples/CRISP/server/src/cli/main.rs index 4c6ba179b8..65c39b3beb 100644 --- a/examples/CRISP/server/src/cli/main.rs +++ b/examples/CRISP/server/src/cli/main.rs @@ -10,7 +10,7 @@ mod commands; use dialoguer::{theme::ColorfulTheme, FuzzySelect, Input}; use reqwest::Client; -use commands::initialize_crisp_round; +use commands::{default_voting_token_hint, initialize_crisp_round}; use crisp::logger::init_logger; use log::info; @@ -42,11 +42,8 @@ struct Cli { enum Commands { /// Initialize new E3 round Init { - #[arg( - short, - long, - default_value = "0x0000000000000000000000000000000000000000" - )] + /// Voting eligibility token (`MockVotingToken` on localhost). Omit or `0x0` to use deploy JSON / `CRISP_VOTING_TOKEN` in `.env`. + #[arg(short, long, default_value = "")] token_address: String, #[arg(short, long, default_value = "1000000000000000000")] balance_threshold: String, @@ -125,7 +122,7 @@ fn select_action() -> Result> { fn get_token_address() -> Result> { Ok(Input::with_theme(&ColorfulTheme::default()) .with_prompt("Enter the token contract address for the voting round") - .default("0x0000000000000000000000000000000000000000".to_string()) + .default(default_voting_token_hint()) .interact_text()?) } diff --git a/examples/CRISP/server/src/config.rs b/examples/CRISP/server/src/config.rs index c4f1a7984e..d2bd976d5a 100644 --- a/examples/CRISP/server/src/config.rs +++ b/examples/CRISP/server/src/config.rs @@ -20,6 +20,10 @@ pub struct Config { pub e3_program_address: String, pub ciphernode_registry_address: String, pub fee_token_address: String, + /// Eligibility token for CRISP rounds (`MockVotingToken` on localhost). Falls back to + /// `packages/crisp-contracts/deployed_contracts.json` when CLI init uses `0x0`. + #[serde(default)] + pub crisp_voting_token: Option, pub chain_id: u64, pub cron_api_key: String, // E3 parameters @@ -34,6 +38,17 @@ pub struct Config { } impl Config { + /// Base URL for outbound HTTP clients (program-server webhooks, CLI, cron). + /// + /// `0.0.0.0` / `::` are bind addresses only; connecting to them fails (e.g. macOS `EADDRNOTAVAIL`). + pub fn enclave_server_url_for_clients(&self) -> String { + Self::client_connectable_url(&self.enclave_server_url) + } + + fn client_connectable_url(url: &str) -> String { + url.replace("0.0.0.0", "127.0.0.1").replace("[::]", "[::1]") + } + pub fn from_env() -> Result { let server_env_path = std::path::Path::new("server/.env"); if server_env_path.exists() { diff --git a/examples/CRISP/server/src/deployments.rs b/examples/CRISP/server/src/deployments.rs new file mode 100644 index 0000000000..4f60f5c6a5 --- /dev/null +++ b/examples/CRISP/server/src/deployments.rs @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Reads `packages/crisp-contracts/deployed_contracts.json` for localhost dev addresses. + +use anyhow::{Context, Result}; +use serde::Deserialize; +use std::path::PathBuf; + +#[derive(Debug, Deserialize)] +struct DeploymentEntry { + address: String, +} + +#[derive(Debug, Deserialize)] +struct ChainDeployments { + #[serde(rename = "CRISPProgram")] + crisp_program: Option, + #[serde(rename = "MockVotingToken")] + mock_voting_token: Option, +} + +#[derive(Debug, Deserialize)] +struct DeployedContractsFile { + localhost: Option, +} + +fn deployments_json_path() -> Result { + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + Ok(manifest_dir + .join("..") + .join("packages") + .join("crisp-contracts") + .join("deployed_contracts.json")) +} + +/// `MockVotingToken` address from the latest localhost deploy, if present. +pub fn localhost_mock_voting_token() -> Result> { + let path = deployments_json_path()?; + if !path.exists() { + return Ok(None); + } + let raw = std::fs::read_to_string(&path).with_context(|| format!("read {}", path.display()))?; + let file: DeployedContractsFile = + serde_json::from_str(&raw).with_context(|| format!("parse {}", path.display()))?; + Ok(file + .localhost + .and_then(|c| c.mock_voting_token) + .map(|e| e.address)) +} + +/// `CRISPProgram` address from the latest localhost deploy, if present. +pub fn localhost_crisp_program() -> Result> { + let path = deployments_json_path()?; + if !path.exists() { + return Ok(None); + } + let raw = std::fs::read_to_string(&path).with_context(|| format!("read {}", path.display()))?; + let file: DeployedContractsFile = + serde_json::from_str(&raw).with_context(|| format!("parse {}", path.display()))?; + Ok(file + .localhost + .and_then(|c| c.crisp_program) + .map(|e| e.address)) +} diff --git a/examples/CRISP/server/src/lib.rs b/examples/CRISP/server/src/lib.rs index 77898ef967..f83796ba1a 100644 --- a/examples/CRISP/server/src/lib.rs +++ b/examples/CRISP/server/src/lib.rs @@ -5,5 +5,6 @@ // or FITNESS FOR A PARTICULAR PURPOSE. pub mod config; +pub mod deployments; pub mod logger; pub mod server; diff --git a/examples/CRISP/server/src/server/indexer.rs b/examples/CRISP/server/src/server/indexer.rs index cd4e713f72..875a1b49c5 100644 --- a/examples/CRISP/server/src/server/indexer.rs +++ b/examples/CRISP/server/src/server/indexer.rs @@ -28,7 +28,7 @@ use e3_sdk::{ }; use evm_helpers::{CRISPContractFactory, InputPublished}; use eyre::Context; -use log::info; +use log::{info, warn}; use num_bigint::BigUint; use std::error::Error; @@ -240,17 +240,37 @@ async fn handle_e3_input_deadline_expiration( repo.update_status("Expired").await?; - if repo.get_vote_count().await? > 0 { - info!("[e3_id={}] Starting computation for E3", e3_id); - repo.update_status("Computing").await?; + let voter_count = repo.get_vote_count().await?; + let votes = repo.get_ciphertext_inputs().await?; + + if voter_count > 0 && votes.is_empty() { + warn!( + "[e3_id={}] {} voter(s) recorded but no InputPublished ciphertexts indexed — \ + skipping FHE compute (check CRISP indexer + on-chain publishInput)", + e3_id, voter_count + ); + repo.update_status("Finished").await?; + info!("[e3_id={}] E3 request handled successfully.", e3_id); + return Ok(()); + } - let votes = repo.get_ciphertext_inputs().await?; + if !votes.is_empty() { + info!( + "[e3_id={}] Starting computation for E3 ({} ciphertext input(s), {} voter(s))", + e3_id, + votes.len(), + voter_count + ); + repo.update_status("Computing").await?; let (id, status) = run_compute( e3_id, e3.e3_params, votes, - format!("{}/state/add-result", CONFIG.enclave_server_url), + format!( + "{}/state/add-result", + CONFIG.enclave_server_url_for_clients() + ), ) .await .map_err(|e| eyre::eyre!("Error sending run compute request: {e}"))?; diff --git a/examples/CRISP/server/src/server/routes/voting.rs b/examples/CRISP/server/src/server/routes/voting.rs index f96f0bd3e6..d7a906c776 100644 --- a/examples/CRISP/server/src/server/routes/voting.rs +++ b/examples/CRISP/server/src/server/routes/voting.rs @@ -6,11 +6,9 @@ use crate::server::{ app_data::AppData, - database::SledDB, models::{ VoteRequest, VoteResponse, VoteResponseStatus, VoteStatusRequest, VoteStatusResponse, }, - repo::CrispE3Repository, CONFIG, }; use actix_web::{web, HttpResponse, Responder}; @@ -113,16 +111,6 @@ async fn broadcast_encrypted_vote( let mut repo = store.e3(vote.round_id); - if !has_voted { - if let Err(e) = repo.insert_voter_address(vote.address.clone()).await { - error!( - "[e3_id={}] Database error inserting voter: {:?}", - vote.round_id, e - ); - return HttpResponse::InternalServerError().json("Internal server error"); - } - } - let e3_id = U256::from(vote.round_id); // encoded_proof is already encoded in JavaScript, just decode from hex @@ -137,14 +125,6 @@ async fn broadcast_encrypted_vote( "[e3_id={}] Failed to decode encoded_proof: {:?}", vote.round_id, e ); - // Rollback voter insertion before returning error - - if !is_vote_update { - let _ = match repo.remove_voter_address(&vote.address).await { - Ok(_) => (), - Err(e) => error!("Error rolling back the vote: {e}"), - }; - } return HttpResponse::BadRequest().json(VoteResponse { status: VoteResponseStatus::FailedBroadcast, @@ -172,6 +152,15 @@ async fn broadcast_encrypted_vote( match contract.publish_input(e3_id, encoded_proof).await { Ok(hash) => { + if !has_voted { + if let Err(e) = repo.insert_voter_address(vote.address.clone()).await { + error!( + "[e3_id={}] Vote on-chain but failed to record voter locally: {:?}", + vote.round_id, e + ); + } + } + let message = if is_vote_update { "Vote Updated Successfully" } else { @@ -188,7 +177,7 @@ async fn broadcast_encrypted_vote( is_vote_update: Some(is_vote_update), }) } - Err(e) => handle_vote_error(e, repo, &vote.address, has_voted).await, + Err(e) => handle_vote_error(e, has_voted).await, } } @@ -223,26 +212,11 @@ fn extract_error_message(e: &Error) -> String { /// # Arguments /// /// * `e` - The error that occurred -/// * `repo` - The repository to rollback -/// * `address` - The address for the vote -/// * `was_update` - Whether this was a vote update (don't rollback if true) -async fn handle_vote_error( - e: Error, - mut repo: CrispE3Repository, - address: &str, - was_update: bool, -) -> HttpResponse { +/// * `was_update` - Whether this was a vote update +async fn handle_vote_error(e: Error, was_update: bool) -> HttpResponse { // Log the full error for debugging error!("Error while sending vote transaction: {:?}", e); - // Only rollback the vote if this was a new vote, not an update - if !was_update { - match repo.remove_voter_address(address).await { - Ok(_) => (), - Err(err) => error!("Error rolling back the vote: {err}"), - }; - } - let user_message = extract_error_message(&e); HttpResponse::InternalServerError().json(VoteResponse { diff --git a/examples/CRISP/test/crisp.spec.ts b/examples/CRISP/test/crisp.spec.ts index cd385bf70c..713a2ece32 100644 --- a/examples/CRISP/test/crisp.spec.ts +++ b/examples/CRISP/test/crisp.spec.ts @@ -8,21 +8,29 @@ import { ConsoleMessage, Page } from '@playwright/test' import { testWithSynpress } from '@synthetixio/synpress' import { MetaMask, metaMaskFixtures } from '@synthetixio/synpress/playwright' import basicSetup from './wallet-setup/basic.setup' -import { execSync } from 'child_process' +import { execFileSync } from 'child_process' import { config } from 'dotenv' import path from 'path' +const CLI = path.join(process.cwd(), 'target', 'debug', 'cli') + config({ path: path.join(process.cwd(), 'server', '.env') }) +config({ path: path.join(process.cwd(), 'client', '.env') }) const E3_DURATION = parseInt(process.env.E3_DURATION as string, 10) * 1000 const OUTPUT_DECRYPTION_WAIT = 80_000 // A small buffer for decryption +function crispTokenAddress(): string { + const tokenAddress = process.env.VITE_CRISP_TOKEN + if (!tokenAddress) { + throw new Error('VITE_CRISP_TOKEN must be set (see client/.env after deploy)') + } + return tokenAddress +} + async function runCliInit(): Promise { try { - // Execute the command and wait for it to complete - const output = execSync('pnpm cli init --token-address 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 --balance-threshold 1000', { - encoding: 'utf-8', - }) + const output = execFileSync(CLI, ['init', '--token-address', crispTokenAddress(), '--balance-threshold', '1000'], { encoding: 'utf-8' }) console.log('Command output:', output) const lines = output.trim().split('\n') const lastLine = lines[lines.length - 1].trim() @@ -39,7 +47,7 @@ async function runCliInit(): Promise { async function checkE3Ready(e3id: number): Promise { try { - const output = execSync(`pnpm cli check-e3-ready --e3id ${e3id}`, { + const output = execFileSync(CLI, ['check-e3-ready', '--e3id', String(e3id)], { encoding: 'utf-8', }) const lines = output.trim().split('\n') @@ -78,6 +86,58 @@ function log(msg: string) { // ConnectKit modal animations + app initialization (initialLoad/switchChain) // can cause the MetaMask button to be detached from the DOM or the page to // navigate while the modal is opening. Retry the whole flow up to 3 times. +// After reload, wagmi/ConnectKit reconnect and App.tsx switchChain can lag on CI. +// Wait until the demo poll is interactive and the wallet session is restored. +async function waitForDemoPollReady(page: Page) { + await page.waitForLoadState('load') + await expect(page.locator("[data-test-id='poll-button-0']")).toBeVisible({ timeout: 60_000 }) + await expect(page.locator('button:has-text("Connect Wallet")')).not.toBeVisible({ timeout: 60_000 }) + await expect(page.locator('.tag.live')).toBeVisible({ timeout: 60_000 }) +} + +async function waitForWalletSession(page: Page) { + log('waiting for wallet session...') + await expect(page.locator('button:has-text("Connect Wallet")')).toHaveCount(0, { timeout: 60_000 }) + // ConnectKit shows a truncated address once wagmi isConnected; VoteManagement only + // sets `user` from the same source, so this gates Cast → signMessage. + await expect(page.locator('button').filter({ hasText: /^0x/i })).toBeVisible({ timeout: 60_000 }) + // Vote status fetch proves user.address + currentRoundId are wired in React context. + await expect(page.locator('.tag').filter({ hasText: 'Checking' })).toHaveCount(0, { timeout: 90_000 }) + log('wallet session ready') +} + +async function reconnectWalletIfNeeded(page: Page, metamask: MetaMask) { + const connectWalletBtn = page.locator('button:has-text("Connect Wallet")') + if (await connectWalletBtn.isVisible({ timeout: 3_000 }).catch(() => false)) { + log('wallet disconnected — reconnecting...') + await connectWalletWithRetry(page) + await metamask.connectToDapp() + } +} + +async function castVoteWithSignature(page: Page, metamask: MetaMask) { + for (let attempt = 1; attempt <= 3; attempt++) { + try { + log(`clicking first vote card (attempt ${attempt})...`) + await page.locator("[data-test-id='poll-button-0']").click() + + const castBtn = page.locator('button:has-text("Cast")') + await expect(castBtn).toBeEnabled({ timeout: 30_000 }) + + log(`clicking Cast Vote (attempt ${attempt})...`) + await castBtn.click() + log(`confirming MetaMask signature request...`) + await metamask.confirmSignature() + return + } catch (error) { + if (attempt === 3) throw error + log(`signature attempt ${attempt} failed, retrying...`) + await page.keyboard.press('Escape').catch(() => {}) + await page.waitForTimeout(2_000) + } + } +} + async function connectWalletWithRetry(page: Page, maxAttempts = 3) { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { @@ -140,14 +200,18 @@ test('CRISP smoke test', async ({ context, page, metamaskPage, extensionId }) => const DKG_DURATION = Date.now() - testStart log(`DKG duration: ${DKG_DURATION}ms`) log(`forcing page reload...`) + const voteStatusReady = page.waitForResponse((resp) => resp.url().includes('/voting/status') && resp.ok(), { timeout: 120_000 }) await page.reload() - - log(`clicking first vote card...`) - await page.locator("[data-test-id='poll-button-0']").click() - log(`clicking Cast Vote...`) - await page.locator('button:has-text("Cast")').click() - log(`confirming MetaMask signature request...`) - await metamask.confirmSignature() + await page.waitForLoadState('load') + log(`ensuring local anvil network after reload...`) + await metamask.switchNetwork('localwallet') + await reconnectWalletIfNeeded(page, metamask) + await waitForDemoPollReady(page) + await waitForWalletSession(page) + await voteStatusReady.catch(() => { + log('vote status response not observed (may have completed before listener)') + }) + await castVoteWithSignature(page, metamask) const WAIT = E3_DURATION - DKG_DURATION + OUTPUT_DECRYPTION_WAIT log(`waiting ${WAIT}ms...`) await page.waitForTimeout(WAIT) diff --git a/examples/CRISP/test/wallet-setup/basic.setup.ts b/examples/CRISP/test/wallet-setup/basic.setup.ts index eff8516340..50525c9476 100644 --- a/examples/CRISP/test/wallet-setup/basic.setup.ts +++ b/examples/CRISP/test/wallet-setup/basic.setup.ts @@ -22,5 +22,5 @@ export default defineWalletSetup(PASSWORD, async (context, walletPage) => { symbol: 'ETH', } await metamask.addNetwork(customNetwork) - // await metamask.switchNetwork("localwallet"); + await metamask.switchNetwork('localwallet') }) diff --git a/package.json b/package.json index bf1be5b1fb..ce19788ac1 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "committee:publish": "cd packages/enclave-contracts && pnpm hardhat committee:publish", "e3:activate": "cd packages/enclave-contracts && pnpm e3:activate", "e3-program:publishInput": "cd packages/enclave-contracts && pnpm hardhat e3-program:publishInput", + "e3-program:setMockEnclave": "cd packages/enclave-contracts && pnpm hardhat e3-program:setMockEnclave", "e3:publishCiphertext": "cd packages/enclave-contracts && pnpm hardhat e3:publishCiphertext", "e3:get-plaintext": "cd packages/enclave-contracts && pnpm e3:get-plaintext", "evm:install": "cd packages/enclave-contracts && pnpm install", diff --git a/packages/enclave-contracts/.gitignore b/packages/enclave-contracts/.gitignore index 26f46e3962..7fb3fcbbdf 100644 --- a/packages/enclave-contracts/.gitignore +++ b/packages/enclave-contracts/.gitignore @@ -55,3 +55,6 @@ ignition/deployments coverage.json package-lock.json pnpm-lock.yaml + +# Benchmark-generated Honk verifiers (secure-8192 etc.; not committed) +contracts/verifiers/bfv/honk/.benchmark/ diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index 6c8c9e317b..843bc79664 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -3159,5 +3159,5 @@ }, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" + "buildInfoId": "solc-0_8_28-834ec0837fe1c9299862cfab43bdd2eef32c6092" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index 5b7ad036bd..b03dc84e7f 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -1062,5 +1062,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" + "buildInfoId": "solc-0_8_28-a6902ac15e994e1c055eb02433db296d1823fdfc" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index 0f8031b08c..0e99886ec2 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -3,6 +3,48 @@ "contractName": "ICiphernodeRegistry", "sourceName": "contracts/interfaces/ICiphernodeRegistry.sol", "abi": [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "pending", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "provided", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nowAt", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityTimelockActive", + "type": "error" + }, + { + "inputs": [], + "name": "AccusationVoteValidityZeroRequiresTimelock", + "type": "error" + }, + { + "inputs": [], + "name": "AttestationBindingCountMismatch", + "type": "error" + }, { "inputs": [], "name": "BondingRegistryNotSet", @@ -64,6 +106,21 @@ "name": "DkgProofRequired", "type": "error" }, + { + "inputs": [], + "name": "FoldAttestationVerifierAlreadySet", + "type": "error" + }, + { + "inputs": [], + "name": "FoldAttestationVerifierNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "FoldAttestationsRequired", + "type": "error" + }, { "inputs": [ { @@ -85,11 +142,26 @@ "name": "InvalidDkgProof", "type": "error" }, + { + "inputs": [], + "name": "InvalidFoldAttestation", + "type": "error" + }, { "inputs": [], "name": "InvalidTicketNumber", "type": "error" }, + { + "inputs": [], + "name": "NoPendingAccusationVoteValidityUpdate", + "type": "error" + }, + { + "inputs": [], + "name": "NoPendingVerifierUpdate", + "type": "error" + }, { "inputs": [], "name": "NodeAlreadySubmitted", @@ -136,6 +208,27 @@ "name": "OnlyEnclave", "type": "error" }, + { + "inputs": [], + "name": "PartyIdNotInProof", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "partyId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "committeeSize", + "type": "uint256" + } + ], + "name": "PartyIdOutOfBounds", + "type": "error" + }, { "inputs": [], "name": "PkCommitmentRequired", @@ -161,11 +254,88 @@ "name": "Unauthorized", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "pending", + "type": "address" + }, + { + "internalType": "address", + "name": "provided", + "type": "address" + } + ], + "name": "VerifierMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nowAt", + "type": "uint256" + } + ], + "name": "VerifierUpdateTimelockActive", + "type": "error" + }, { "inputs": [], "name": "ZeroAddress", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "accusationVoteValidity", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityProposalCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "accusationVoteValidity", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "accusationVoteValidity", + "type": "uint256" + } + ], + "name": "AccusationVoteValiditySet", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -408,6 +578,51 @@ "name": "CommitteeViabilityUpdated", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "verifier", + "type": "address" + } + ], + "name": "DkgFoldAttestationVerifierProposalCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "verifier", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + } + ], + "name": "DkgFoldAttestationVerifierProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "verifier", + "type": "address" + } + ], + "name": "DkgFoldAttestationVerifierUpdated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -490,6 +705,19 @@ "name": "TicketSubmitted", "type": "event" }, + { + "inputs": [], + "name": "accusationVoteValidity", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -503,6 +731,50 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "cancelAccusationVoteValidityProposal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "partyId", + "type": "uint256" + } + ], + "name": "canonicalCommitteeNodeAt", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_accusationVoteValidity", + "type": "uint256" + } + ], + "name": "commitAccusationVoteValidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -703,6 +975,35 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "getDkgAnchors", + "outputs": [ + { + "internalType": "uint256[]", + "name": "partyIds", + "type": "uint256[]" + }, + { + "internalType": "bytes32[]", + "name": "skAggCommits", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "esmAggCommits", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -808,6 +1109,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_accusationVoteValidity", + "type": "uint256" + } + ], + "name": "proposeAccusationVoteValidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -829,6 +1143,11 @@ "internalType": "bytes", "name": "proof", "type": "bytes" + }, + { + "internalType": "bytes", + "name": "dkgAttestationBundle", + "type": "bytes" } ], "name": "publishCommittee", @@ -910,6 +1229,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_accusationVoteValidity", + "type": "uint256" + } + ], + "name": "setAccusationVoteValidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1000,5 +1332,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" + "buildInfoId": "solc-0_8_28-a6902ac15e994e1c055eb02433db296d1823fdfc" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index eaf85bfd74..356890b821 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -2427,5 +2427,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" + "buildInfoId": "solc-0_8_28-a6902ac15e994e1c055eb02433db296d1823fdfc" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index 6eba16da9c..a229906067 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -108,6 +108,11 @@ "name": "OperatorUnderSlash", "type": "error" }, + { + "inputs": [], + "name": "PartyIdNotInDkgAnchors", + "type": "error" + }, { "inputs": [], "name": "ProofIsValid", @@ -640,6 +645,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "ciphernodeRegistry", + "outputs": [ + { + "internalType": "contract ICiphernodeRegistry", + "name": "registry", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -964,6 +982,35 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "partyId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + } + ], + "name": "proposeSlashByDkgParty", + "outputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1186,5 +1233,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" + "buildInfoId": "solc-0_8_28-834ec0837fe1c9299862cfab43bdd2eef32c6092" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index b0bec68a52..f58fbbe9a8 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -8,6 +8,48 @@ "stateMutability": "nonpayable", "type": "constructor" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "pending", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "provided", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nowAt", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityTimelockActive", + "type": "error" + }, + { + "inputs": [], + "name": "AccusationVoteValidityZeroRequiresTimelock", + "type": "error" + }, + { + "inputs": [], + "name": "AttestationBindingCountMismatch", + "type": "error" + }, { "inputs": [], "name": "BondingRegistryNotSet", @@ -74,6 +116,21 @@ "name": "DkgProofRequired", "type": "error" }, + { + "inputs": [], + "name": "FoldAttestationVerifierAlreadySet", + "type": "error" + }, + { + "inputs": [], + "name": "FoldAttestationVerifierNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "FoldAttestationsRequired", + "type": "error" + }, { "inputs": [ { @@ -95,6 +152,11 @@ "name": "InvalidDkgProof", "type": "error" }, + { + "inputs": [], + "name": "InvalidFoldAttestation", + "type": "error" + }, { "inputs": [], "name": "InvalidInitialization", @@ -105,6 +167,16 @@ "name": "InvalidTicketNumber", "type": "error" }, + { + "inputs": [], + "name": "NoPendingAccusationVoteValidityUpdate", + "type": "error" + }, + { + "inputs": [], + "name": "NoPendingVerifierUpdate", + "type": "error" + }, { "inputs": [], "name": "NodeAlreadySubmitted", @@ -178,6 +250,27 @@ "name": "OwnableUnauthorizedAccount", "type": "error" }, + { + "inputs": [], + "name": "PartyIdNotInProof", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "partyId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "committeeSize", + "type": "uint256" + } + ], + "name": "PartyIdOutOfBounds", + "type": "error" + }, { "inputs": [], "name": "PkCommitmentRequired", @@ -219,11 +312,88 @@ "name": "Unauthorized", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "pending", + "type": "address" + }, + { + "internalType": "address", + "name": "provided", + "type": "address" + } + ], + "name": "VerifierMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nowAt", + "type": "uint256" + } + ], + "name": "VerifierUpdateTimelockActive", + "type": "error" + }, { "inputs": [], "name": "ZeroAddress", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "accusationVoteValidity", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityProposalCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "accusationVoteValidity", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "accusationVoteValidity", + "type": "uint256" + } + ], + "name": "AccusationVoteValiditySet", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -479,6 +649,51 @@ "name": "CommitteeViabilityUpdated", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "verifier", + "type": "address" + } + ], + "name": "DkgFoldAttestationVerifierProposalCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "verifier", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + } + ], + "name": "DkgFoldAttestationVerifierProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "verifier", + "type": "address" + } + ], + "name": "DkgFoldAttestationVerifierUpdated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -638,6 +853,45 @@ "name": "TicketSubmitted", "type": "event" }, + { + "inputs": [], + "name": "ACCUSATION_VOTE_VALIDITY_TIMELOCK", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DEFAULT_ACCUSATION_VOTE_VALIDITY", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DKG_FOLD_VERIFIER_TIMELOCK", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "MAX_CIPHERNODE_LEAVES", @@ -697,6 +951,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "accusationVoteValidity", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -723,6 +990,44 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "cancelAccusationVoteValidityProposal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "cancelDkgFoldAttestationVerifierProposal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "partyId", + "type": "uint256" + } + ], + "name": "canonicalCommitteeNodeAt", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -779,6 +1084,32 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_accusationVoteValidity", + "type": "uint256" + } + ], + "name": "commitAccusationVoteValidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IDkgFoldAttestationVerifier", + "name": "verifier", + "type": "address" + } + ], + "name": "commitDkgFoldAttestationVerifier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -798,6 +1129,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "dkgFoldAttestationVerifier", + "outputs": [ + { + "internalType": "contract IDkgFoldAttestationVerifier", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "enclave", @@ -992,6 +1336,35 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "getDkgAnchors", + "outputs": [ + { + "internalType": "uint256[]", + "name": "partyIds", + "type": "uint256[]" + }, + { + "internalType": "bytes32[]", + "name": "skAggCommits", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "esmAggCommits", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1141,6 +1514,58 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "pendingAccusationVoteValidity", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingAccusationVoteValidityAt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingDkgFoldAttestationVerifier", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingDkgFoldAttestationVerifierAt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "pendingOwner", @@ -1154,6 +1579,32 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_accusationVoteValidity", + "type": "uint256" + } + ], + "name": "proposeAccusationVoteValidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IDkgFoldAttestationVerifier", + "name": "verifier", + "type": "address" + } + ], + "name": "proposeDkgFoldAttestationVerifier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1194,6 +1645,11 @@ "internalType": "bytes", "name": "proof", "type": "bytes" + }, + { + "internalType": "bytes", + "name": "dkgAttestationBundle", + "type": "bytes" } ], "name": "publishCommittee", @@ -1301,6 +1757,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_accusationVoteValidity", + "type": "uint256" + } + ], + "name": "setAccusationVoteValidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1327,6 +1796,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "contract IDkgFoldAttestationVerifier", + "name": "verifier", + "type": "address" + } + ], + "name": "setInitialDkgFoldAttestationVerifier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1443,30 +1925,30 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600f57600080fd5b506016601a565b60ca565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560695760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b613dd7806100d96000396000f3fe608060405234801561001057600080fd5b50600436106102255760003560e01c806301ffc9a71461022a578063096b810a14610252578063099a161a146102675780630f3e34121461028857806317d611201461029b5780631e08d0e8146102bc5780632800d829146102c4578063291a691b146102d75780632e7b716d146102ea5780634d6861a6146102fd57806350e6d94c146103105780635d5047761461033357806370e36bbe14610346578063715018a61461035957806379ba5097146103615780637c92f5241461036957806385814243146103965780638a78bb15146103b65780638cb89ecb146103c95780638d1ddfb1146103e95780638da5cb5b146103ff5780638e5ce3ad146104075780639015d3711461041a5780639a7a2ffc1461042d5780639f0f874a1461046a578063a016493014610473578063a8a4d69b14610493578063bbe4b803146104a6578063bff232c1146104b0578063c2b40ae4146104c3578063c3a0ec30146104e3578063c6b2a438146104f4578063ca2869a014610507578063cd6dc68714610527578063cf90b6ed1461053a578063da881e5a14610544578063dbb06c9314610557578063e30c39781461056a578063e59e469514610572578063e6745e1314610585578063e82f3b7014610598578063ebf0c717146105ab578063f1650536146105b3578063f2fde38b146105cd578063f379b0df146105e0578063f52fd8031461061a578063f6fc05d51461068b575b600080fd5b61023d6102383660046132f8565b610694565b60405190151581526020015b60405180910390f35b610265610260366004613337565b6106cb565b005b61027a610275366004613354565b61080a565b604051908152602001610249565b610265610296366004613354565b610844565b6102ae6102a9366004613354565b6108be565b6040516102499291906133e4565b61027a600181565b61027a6102d2366004613354565b610a6e565b61023d6102e5366004613412565b610abb565b61023d6102f8366004613337565b610c9c565b61023d61030b366004613354565b610d4d565b61023d61031e366004613337565b60066020526000908152604090205460ff1681565b61023d61034136600461344f565b610d8e565b610265610354366004613337565b610dd3565b610265610e4a565b610265610e6e565b61037c61037736600461347f565b610ead565b6040805192835263ffffffff909116602083015201610249565b6001546103a9906001600160a01b031681565b60405161024991906134b7565b6102656103c4366004613337565b611058565b61027a6103d7366004613354565b60096020526000908152604090205481565b600454600160281b900464ffffffffff1661027a565b6103a96111a3565b600b546103a9906001600160a01b031681565b61023d610428366004613337565b6111be565b61045461043b366004613337565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff9091168152602001610249565b61027a60035481565b610486610481366004613354565b6111dc565b60405161024991906134cb565b61023d6104a136600461344f565b611275565b61027a6210000081565b6102656104be366004613337565b6112ba565b61027a6104d1366004613354565b60086020526000908152604090205481565b6001546001600160a01b03166103a9565b610265610502366004613526565b611333565b61027a610515366004613354565b60009081526008602052604090205490565b6102656105353660046135ac565b611624565b61027a62093a8081565b61023d610552366004613354565b611783565b6000546103a9906001600160a01b031681565b6103a9611a6f565b610265610580366004613337565b611a7a565b6102656105933660046135d8565b611af3565b61027a6105a6366004613354565b611cb5565b61027a611ce7565b6105bb601481565b60405160ff9091168152602001610249565b6102656105db366004613337565b611cfa565b6004546105fc9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff938416815292909116602083015201610249565b61065c610628366004613354565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b604051610249949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61027a60025481565b60006001600160e01b0319821663cb54661360e01b14806106c557506001600160e01b031982166301ffc9a760e01b145b92915050565b6106d36111a3565b6001600160a01b0316336001600160a01b031614806106fc57506001546001600160a01b031633145b61071957604051632864c4e160e01b815260040160405180910390fd5b610722816111be565b819061074b576040516381e5828960e01b815260040161074291906134b7565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff169061077a9060049083611d6b565b6001600160a01b0382166000908152600660205260408120805460ff1916905560028054916107a883613610565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5926107fe92869291600160281b900464ffffffffff1690613627565b60405180910390a25050565b6000818152600a60205260408120600481015461083a576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61084c611fb7565b60018110158015610860575062093a808111155b81906108825760405163028237cd60e61b815260040161074291815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b038111156108f5576108f5613648565b60405190808252806020026020018201604052801561091e578160200160208202803683370190505b509450806001600160401b0381111561093957610939613648565b604051908082528060200260200182016040528015610962578160200160208202803683370190505b5093506000805b83811015610a645760008560060182815481106109885761098861365e565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff1660028111156109d0576109d0613674565b03610a5b57808884815181106109e8576109e861365e565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610a4257610a4261365e565b602090810291909101015282610a578161368a565b9350505b50600101610969565b5050505050915091565b6000818152600a6020526040812081815460ff166003811115610a9357610a93613674565b03610ab157604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b03163314610ae75760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff166003811115610b0c57610b0c613674565b14610b2a576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b9891906136a3565b905080610bab60408601602087016136d0565b63ffffffff161115610bc360408601602087016136d0565b829091610bf1576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610742565b5050815460ff19166001908117835582018590554260028301819055600354610c19916136eb565b6003830155610c2d60058301856002613241565b50610c36611ce7565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610c88928a928a92916136fe565b60405180910390a250600195945050505050565b6000610ca7826111be565b610cb357506000919050565b6001546001600160a01b0316610cdc576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610d0c9085906004016134b7565b602060405180830381865afa158015610d29573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c5919061375f565b6000818152600a602052604081206001815460ff166003811115610d7357610d73613674565b14610d815750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115610dcb57610dcb613674565b149392505050565b610ddb611fb7565b6001600160a01b038116610e025760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610e52611fb7565b6040516001623f026d60e01b0319815260040160405180910390fd5b3380610e78611a6f565b6001600160a01b031614610ea1578060405163118cdaa760e01b815260040161074291906134b7565b610eaa81611feb565b50565b600b5460009081906001600160a01b03163314610edd5760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff166003811115610f0357610f03613674565b14610f2157604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff166002811115610f6257610f62613674565b14610f7257600b01549150611050565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b8201805491610fa783613610565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610ff8929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6110606111a3565b6001600160a01b0316336001600160a01b0316148061108957506001546001600160a01b031633145b6110a657604051632864c4e160e01b815260040160405180910390fd5b6110af816111be565b610eaa57600454600160281b900464ffffffffff166210000081106110e7576040516335b4ac3f60e01b815260040160405180910390fd5b6110fb60046001600160a01b038416612012565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161114d8361368a565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53926107fe92869291600160281b900464ffffffffff1690613627565b6000806111ae61218d565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b6000818152600a60205260409020600481015460609190611210576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561126857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161124a575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156112b1576112b1613674565b14159392505050565b6112c2611fb7565b6001600160a01b0381166112e95760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b6000868152600a602052604090206002815460ff16600381111561135957611359613674565b1461137757604051634f4b461f60e11b815260040160405180910390fd5b60048101541561139a5760405163632a22bb60e01b815260040160405180910390fd5b836113b857604051636caad1ed60e11b815260040160405180910390fd5b600061141f8260060180548060200260200160405190810160405280929190818152602001828054801561141557602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116113f7575b50505050506121b1565b60078301819055600480840187905560008a8152600960205260408082208990558154905163101bb4d760e21b81529283018c9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015611488573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114b091908101906138f2565b9050806101c001511561156f57836114db57604051630fb0193f60e41b815260040160405180910390fd5b61012081015160008a815260086020526040908190205490516303a0d4ed60e11b81526001600160a01b0390921691630741a9da9161152c918d919060068901908c9089908d908d90600401613abf565b602060405180830381865afa158015611549573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061156d919061375f565b505b6000546040516340a3b76160e11b8152600481018b9052602481018890526001600160a01b03909116906381476ec290604401600060405180830381600087803b1580156115bc57600080fd5b505af11580156115d0573d6000803e3d6000fd5b50505050887fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018a8a8a8a8a60405161161196959493929190613b0b565b60405180910390a2505050505050505050565b600061162e6121e1565b805490915060ff600160401b82041615906001600160401b03166000811580156116555750825b90506000826001600160401b031660011480156116715750303b155b90508115801561167f575080155b1561169d5760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b031916600117855583156116c657845460ff60401b1916600160401b1785555b6001600160a01b0387166116ed5760405163d92e233d60e01b815260040160405180910390fd5b6116f63361220a565b6117026004601461221b565b61170b86610844565b6117136111a3565b6001600160a01b0316876001600160a01b0316146117345761173487611feb565b831561177a57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff1660038111156117a8576117a8613674565b036117c657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156117de576117de613674565b146117fc57604051631860f69960e31b815260040160405180910390fd5b8060030154421161182057604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061190c578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b1580156118ea57600080fd5b505af11580156118fe573d6000803e3d6000fd5b506000979650505050505050565b815460ff191660021782556006820154600b83018190556000816001600160401b0381111561193d5761193d613648565b604051908082528060200260200182016040528015611966578160200160208202803683370190505b50905060005b828110156119db5784600901600086600601838154811061198f5761198f61365e565b60009182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106119c8576119c861365e565b602090810291909101015260010161196c565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b158015611a2257600080fd5b505af1158015611a36573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e58560060183604051610c88929190613b59565b6000806111ae61225a565b611a82611fb7565b6001600160a01b038116611aa95760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff166003811115611b1857611b18613674565b03611b3657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611b4e57611b4e613674565b14611b6c57604051631860f69960e31b815260040160405180910390fd5b8060030154421115611b9157604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff1615611bc45760405163257309f160e11b815260040160405180910390fd5b611bcd33610c9c565b611bea5760405163149fbcfd60e11b815260040160405180910390fd5b611bf533838561227e565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff19166001179055909150611c719083908361245a565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b60008181526009602052604090205480611ce2576040516322e679e360e11b815260040160405180910390fd5b919050565b6000611cf560046014612666565b905090565b611d02611fb7565b6000611d0c61225a565b80546001600160a01b0319166001600160a01b0384169081178255909150611d326111a3565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020613dab8339815191528210611d8557600080fd5b825464ffffffffff600160281b90910481169082168111611da557600080fd5b8260005b81866001016000611dba84886126cc565b64ffffffffff168152602001908152602001600020819055506000816001611de29190613b6c565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611e175750611faf565b60018516600003611ee3576000611e3883611e33886001613b85565b6126cc565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e9a91600401613ba2565b602060405180830381865af4158015611eb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611edb91906136a3565b935050611f9b565b6000611ef483611e33600189613bd3565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611f5691600401613ba2565b602060405180830381865af4158015611f73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9791906136a3565b9350505b50647fffffffff600194851c169301611da9565b505050505050565b33611fc06111a3565b6001600160a01b031614611fe9573360405163118cdaa760e01b815260040161074291906134b7565b565b6000611ff561225a565b80546001600160a01b0319168155905061200e826126ea565b5050565b8154600160281b900464ffffffffff16600080516020613dab833981519152821061203c57600080fd5b825464ffffffffff9081169082161061205457600080fd5b61205f816001613b85565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b8185600101600061209684876126cc565b64ffffffffff16815260208101919091526040016000205560018316156121865760006120c882611e33600187613bd3565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161212a91600401613ba2565b602060405180830381865af4158015612147573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216b91906136a3565b647fffffffff600195861c1694909350919091019050612085565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000816040516020016121c49190613bf0565b604051602081830303815290604052805190602001209050919050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006106c5565b612212612746565b610eaa8161276b565b602060ff8216111561222c57600080fd5b61223d600160ff831681901b613c24565b82546001600160501b03191664ffffffffff919091161790915550565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b6000821161229f5760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166122c8576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd719188916122ff91613c24565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612348573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236c91906136a3565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123e791906136a3565b90506000811161240a5760405163aeaddff160e01b815260040160405180910390fd5b60006124168284613c37565b9050600081116124395760405163149fbcfd60e11b815260040160405180910390fd5b8086111561177a5760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff16908111156124da57508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff191682179055905061265f565b600080876009016000856000815481106124f6576124f661365e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b84548110156125825760008960090160008784815481106125435761254361365e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612579578092508193505b50600101612520565b5080861061259757600094505050505061265f565b600088600a0160008685815481106125b1576125b161365e565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156125ef576125ef613674565b0217905550868483815481106126075761260761365e565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff161161267757600080fd5b602060ff8316111561268857600080fd5b8254600160281b900464ffffffffff16806126a760ff85166002613d69565b64ffffffffff1610156126b957600080fd5b6126c484828561279d565b949350505050565b6000816126e060ff851663ffffffff613d83565b61265f9190613b85565b60006126f461218d565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b61274e612855565b611fe957604051631afcd79f60e31b815260040160405180910390fd5b612773612746565b6001600160a01b038116610ea1576000604051631e4fbdf760e01b815260040161074291906134b7565b6000602060ff831611156127b057600080fd5b8264ffffffffff166000036127cf576127c88261286f565b905061265f565b60006127dc836001613b6c565b60ff166001600160401b038111156127f6576127f6613648565b60405190808252806020026020018201604052801561281f578160200160208202803683370190505b50905061282e85858584612ec4565b808360ff16815181106128435761284361365e565b60200260200101519150509392505050565b600061285f6121e1565b54600160401b900460ff16919050565b60008160ff1660000361288457506000919050565b8160ff166001036128b657507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036128e857507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361291a57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361294c57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361297e57507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036129b057507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036129e257507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612a1457507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612a4657507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a03612a7857507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612aaa57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612adc57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612b0e57507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612b4057507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612b7257507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612ba457507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612bd657507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612c0857507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612c3a57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612c6c57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612c9e57507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612cd057507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612d0257507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612d3457507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612d6657507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612d9857507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612dca57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612dfc57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612e2e57507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612e6057507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612e9257507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361022557507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff83161115612ed557600080fd5b60008364ffffffffff1611612ee957600080fd5b6000612ef6600185613bd3565b905060018116600003612f4e57846001016000612f146000846126cc565b64ffffffffff1681526020019081526020016000205482600081518110612f3d57612f3d61365e565b602002602001018181525050612f78565b612f58600061286f565b82600081518110612f6b57612f6b61365e565b6020026020010181815250505b60005b8360ff168160ff161015611faf57600182166000036130745773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612fce57612fce61365e565b60200260200101518152602001612fe48561286f565b8152506040518263ffffffff1660e01b81526004016130039190613ba2565b602060405180830381865af4158015613020573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061304491906136a3565b83613050836001613b6c565b60ff16815181106130635761306361365e565b60200260200101818152505061322e565b6000613081826001613b6c565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156131265760008760010160006130da8560016130c99190613b6c565b60018864ffffffffff16901c6126cc565b64ffffffffff16815260200190815260200160002054905080858460016131019190613b6c565b60ff16815181106131145761311461365e565b6020026020010181815250505061322c565b600087600101600061313f85600188611e339190613bd3565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106131975761319761365e565b60200260200101518152506040518263ffffffff1660e01b81526004016131be9190613ba2565b602060405180830381865af41580156131db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131ff91906136a3565b8561320b856001613b6c565b60ff168151811061321e5761321e61365e565b602002602001018181525050505b505b647fffffffff600192831c169101612f7b565b6001830191839082156132d35791602002820160005b838211156132a157833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613257565b80156132d15782816101000a81549063ffffffff02191690556004016020816003010492830192600103026132a1565b505b506132df9291506132e3565b5090565b5b808211156132df57600081556001016132e4565b60006020828403121561330a57600080fd5b81356001600160e01b03198116811461265f57600080fd5b6001600160a01b0381168114610eaa57600080fd5b60006020828403121561334957600080fd5b813561265f81613322565b60006020828403121561336657600080fd5b5035919050565b600081518084526020840193506020830160005b828110156133a85781516001600160a01b0316865260209586019590910190600101613381565b5093949350505050565b600081518084526020840193506020830160005b828110156133a85781518652602095860195909101906001016133c6565b6040815260006133f7604083018561336d565b828103602084015261340981856133b2565b95945050505050565b60008060006080848603121561342757600080fd5b83359250602084013591506080840185101561344257600080fd5b6040840190509250925092565b6000806040838503121561346257600080fd5b82359150602083013561347481613322565b809150509250929050565b60008060006060848603121561349457600080fd5b8335925060208401356134a681613322565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b60208152600061265f602083018461336d565b60008083601f8401126134f057600080fd5b5081356001600160401b0381111561350757600080fd5b60208301915083602082850101111561351f57600080fd5b9250929050565b6000806000806000806080878903121561353f57600080fd5b8635955060208701356001600160401b0381111561355c57600080fd5b61356889828a016134de565b9096509450506040870135925060608701356001600160401b0381111561358e57600080fd5b61359a89828a016134de565b979a9699509497509295939492505050565b600080604083850312156135bf57600080fd5b82356135ca81613322565b946020939093013593505050565b600080604083850312156135eb57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b60008161361f5761361f6135fa565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b60006001820161369c5761369c6135fa565b5060010190565b6000602082840312156136b557600080fd5b5051919050565b803563ffffffff81168114611ce257600080fd5b6000602082840312156136e257600080fd5b61265f826136bc565b808201808211156106c5576106c56135fa565b84815260a08101602082018560005b600281101561373a5763ffffffff613724836136bc565b168352602092830192919091019060010161370d565b50505060608201939093526080015292915050565b80518015158114611ce257600080fd5b60006020828403121561377157600080fd5b61265f8261374f565b6040516101e081016001600160401b038111828210171561379d5761379d613648565b60405290565b604051601f8201601f191681016001600160401b03811182821017156137cb576137cb613648565b604052919050565b805160048110611ce257600080fd5b600082601f8301126137f357600080fd5b604080519081016001600160401b038111828210171561381557613815613648565b806040525080604084018581111561382c57600080fd5b845b8181101561384657805183526020928301920161382e565b509195945050505050565b8051611ce281613322565b805160ff81168114611ce257600080fd5b600082601f83011261387e57600080fd5b81516001600160401b0381111561389757613897613648565b6138aa601f8201601f19166020016137a3565b8181528460208386010111156138bf57600080fd5b60005b828110156138de576020818601810151838301820152016138c2565b506000918101602001919091529392505050565b60006020828403121561390457600080fd5b81516001600160401b0381111561391a57600080fd5b8201610200818503121561392d57600080fd5b61393561377a565b81518152613945602083016137d3565b60208201526040828101519082015261396185606084016137e2565b606082015260a0820151608082015261397c60c08301613851565b60a082015261398d60e0830161385c565b60c08201526101008201516001600160401b038111156139ac57600080fd5b6139b88682850161386d565b60e0830152506139cb6101208301613851565b6101008201526139de6101408301613851565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613a1557600080fd5b613a218682850161386d565b61018083015250613a356101c08301613851565b6101a0820152613a486101e0830161374f565b6101c0820152949350505050565b6000815480845260208401935082600052602060002060005b828110156133a85781546001600160a01b0316865260209095019460019182019101613a6f565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b87815286602082015260c060408201526000613ade60c0830188613a56565b86606084015285608084015282810360a0840152613afd818587613a96565b9a9950505050505050505050565b608081526000613b1e6080830189613a56565b8281036020840152613b3181888a613a96565b90508560408401528281036060840152613b4c818587613a96565b9998505050505050505050565b6040815260006133f76040830185613a56565b60ff81811683821601908111156106c5576106c56135fa565b64ffffffffff81811683821601908111156106c5576106c56135fa565b60408101818360005b6002811015613bca578151835260209283019290910190600101613bab565b50505092915050565b64ffffffffff82811682821603908111156106c5576106c56135fa565b8151600090829060208501835b828110156138465781516001600160a01b0316845260209384019390910190600101613bfd565b818103818111156106c5576106c56135fa565b600082613c5457634e487b7160e01b600052601260045260246000fd5b500490565b6001815b600184111561105057808504811115613c7857613c786135fa565b6001841615613c8657908102905b60019390931c928002613c5d565b600082613ca3575060016106c5565b81613cb0575060006106c5565b8160018114613cc65760028114613cd057613d02565b60019150506106c5565b60ff841115613ce157613ce16135fa565b6001841b915064ffffffffff821115613cfc57613cfc6135fa565b506106c5565b5060208310610133831016604e8410600b8410161715613d3a575081810a64ffffffffff811115613d3557613d356135fa565b6106c5565b613d4a64ffffffffff8484613c59565b8064ffffffffff04821115613d6157613d616135fa565b029392505050565b600061265f64ffffffffff841664ffffffffff8416613c94565b64ffffffffff8181168382160290811690818114613da357613da36135fa565b509291505056fe30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102255760003560e01c806301ffc9a71461022a578063096b810a14610252578063099a161a146102675780630f3e34121461028857806317d611201461029b5780631e08d0e8146102bc5780632800d829146102c4578063291a691b146102d75780632e7b716d146102ea5780634d6861a6146102fd57806350e6d94c146103105780635d5047761461033357806370e36bbe14610346578063715018a61461035957806379ba5097146103615780637c92f5241461036957806385814243146103965780638a78bb15146103b65780638cb89ecb146103c95780638d1ddfb1146103e95780638da5cb5b146103ff5780638e5ce3ad146104075780639015d3711461041a5780639a7a2ffc1461042d5780639f0f874a1461046a578063a016493014610473578063a8a4d69b14610493578063bbe4b803146104a6578063bff232c1146104b0578063c2b40ae4146104c3578063c3a0ec30146104e3578063c6b2a438146104f4578063ca2869a014610507578063cd6dc68714610527578063cf90b6ed1461053a578063da881e5a14610544578063dbb06c9314610557578063e30c39781461056a578063e59e469514610572578063e6745e1314610585578063e82f3b7014610598578063ebf0c717146105ab578063f1650536146105b3578063f2fde38b146105cd578063f379b0df146105e0578063f52fd8031461061a578063f6fc05d51461068b575b600080fd5b61023d6102383660046132f8565b610694565b60405190151581526020015b60405180910390f35b610265610260366004613337565b6106cb565b005b61027a610275366004613354565b61080a565b604051908152602001610249565b610265610296366004613354565b610844565b6102ae6102a9366004613354565b6108be565b6040516102499291906133e4565b61027a600181565b61027a6102d2366004613354565b610a6e565b61023d6102e5366004613412565b610abb565b61023d6102f8366004613337565b610c9c565b61023d61030b366004613354565b610d4d565b61023d61031e366004613337565b60066020526000908152604090205460ff1681565b61023d61034136600461344f565b610d8e565b610265610354366004613337565b610dd3565b610265610e4a565b610265610e6e565b61037c61037736600461347f565b610ead565b6040805192835263ffffffff909116602083015201610249565b6001546103a9906001600160a01b031681565b60405161024991906134b7565b6102656103c4366004613337565b611058565b61027a6103d7366004613354565b60096020526000908152604090205481565b600454600160281b900464ffffffffff1661027a565b6103a96111a3565b600b546103a9906001600160a01b031681565b61023d610428366004613337565b6111be565b61045461043b366004613337565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff9091168152602001610249565b61027a60035481565b610486610481366004613354565b6111dc565b60405161024991906134cb565b61023d6104a136600461344f565b611275565b61027a6210000081565b6102656104be366004613337565b6112ba565b61027a6104d1366004613354565b60086020526000908152604090205481565b6001546001600160a01b03166103a9565b610265610502366004613526565b611333565b61027a610515366004613354565b60009081526008602052604090205490565b6102656105353660046135ac565b611624565b61027a62093a8081565b61023d610552366004613354565b611783565b6000546103a9906001600160a01b031681565b6103a9611a6f565b610265610580366004613337565b611a7a565b6102656105933660046135d8565b611af3565b61027a6105a6366004613354565b611cb5565b61027a611ce7565b6105bb601481565b60405160ff9091168152602001610249565b6102656105db366004613337565b611cfa565b6004546105fc9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff938416815292909116602083015201610249565b61065c610628366004613354565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b604051610249949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61027a60025481565b60006001600160e01b0319821663cb54661360e01b14806106c557506001600160e01b031982166301ffc9a760e01b145b92915050565b6106d36111a3565b6001600160a01b0316336001600160a01b031614806106fc57506001546001600160a01b031633145b61071957604051632864c4e160e01b815260040160405180910390fd5b610722816111be565b819061074b576040516381e5828960e01b815260040161074291906134b7565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff169061077a9060049083611d6b565b6001600160a01b0382166000908152600660205260408120805460ff1916905560028054916107a883613610565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5926107fe92869291600160281b900464ffffffffff1690613627565b60405180910390a25050565b6000818152600a60205260408120600481015461083a576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61084c611fb7565b60018110158015610860575062093a808111155b81906108825760405163028237cd60e61b815260040161074291815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b038111156108f5576108f5613648565b60405190808252806020026020018201604052801561091e578160200160208202803683370190505b509450806001600160401b0381111561093957610939613648565b604051908082528060200260200182016040528015610962578160200160208202803683370190505b5093506000805b83811015610a645760008560060182815481106109885761098861365e565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff1660028111156109d0576109d0613674565b03610a5b57808884815181106109e8576109e861365e565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610a4257610a4261365e565b602090810291909101015282610a578161368a565b9350505b50600101610969565b5050505050915091565b6000818152600a6020526040812081815460ff166003811115610a9357610a93613674565b03610ab157604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b03163314610ae75760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff166003811115610b0c57610b0c613674565b14610b2a576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b9891906136a3565b905080610bab60408601602087016136d0565b63ffffffff161115610bc360408601602087016136d0565b829091610bf1576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610742565b5050815460ff19166001908117835582018590554260028301819055600354610c19916136eb565b6003830155610c2d60058301856002613241565b50610c36611ce7565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610c88928a928a92916136fe565b60405180910390a250600195945050505050565b6000610ca7826111be565b610cb357506000919050565b6001546001600160a01b0316610cdc576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610d0c9085906004016134b7565b602060405180830381865afa158015610d29573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c5919061375f565b6000818152600a602052604081206001815460ff166003811115610d7357610d73613674565b14610d815750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115610dcb57610dcb613674565b149392505050565b610ddb611fb7565b6001600160a01b038116610e025760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610e52611fb7565b6040516001623f026d60e01b0319815260040160405180910390fd5b3380610e78611a6f565b6001600160a01b031614610ea1578060405163118cdaa760e01b815260040161074291906134b7565b610eaa81611feb565b50565b600b5460009081906001600160a01b03163314610edd5760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff166003811115610f0357610f03613674565b14610f2157604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff166002811115610f6257610f62613674565b14610f7257600b01549150611050565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b8201805491610fa783613610565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610ff8929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6110606111a3565b6001600160a01b0316336001600160a01b0316148061108957506001546001600160a01b031633145b6110a657604051632864c4e160e01b815260040160405180910390fd5b6110af816111be565b610eaa57600454600160281b900464ffffffffff166210000081106110e7576040516335b4ac3f60e01b815260040160405180910390fd5b6110fb60046001600160a01b038416612012565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161114d8361368a565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53926107fe92869291600160281b900464ffffffffff1690613627565b6000806111ae61218d565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b6000818152600a60205260409020600481015460609190611210576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561126857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161124a575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156112b1576112b1613674565b14159392505050565b6112c2611fb7565b6001600160a01b0381166112e95760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b6000868152600a602052604090206002815460ff16600381111561135957611359613674565b1461137757604051634f4b461f60e11b815260040160405180910390fd5b60048101541561139a5760405163632a22bb60e01b815260040160405180910390fd5b836113b857604051636caad1ed60e11b815260040160405180910390fd5b600061141f8260060180548060200260200160405190810160405280929190818152602001828054801561141557602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116113f7575b50505050506121b1565b60078301819055600480840187905560008a8152600960205260408082208990558154905163101bb4d760e21b81529283018c9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015611488573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114b091908101906138f2565b9050806101c001511561156f57836114db57604051630fb0193f60e41b815260040160405180910390fd5b61012081015160008a815260086020526040908190205490516303a0d4ed60e11b81526001600160a01b0390921691630741a9da9161152c918d919060068901908c9089908d908d90600401613abf565b602060405180830381865afa158015611549573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061156d919061375f565b505b6000546040516340a3b76160e11b8152600481018b9052602481018890526001600160a01b03909116906381476ec290604401600060405180830381600087803b1580156115bc57600080fd5b505af11580156115d0573d6000803e3d6000fd5b50505050887fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018a8a8a8a8a60405161161196959493929190613b0b565b60405180910390a2505050505050505050565b600061162e6121e1565b805490915060ff600160401b82041615906001600160401b03166000811580156116555750825b90506000826001600160401b031660011480156116715750303b155b90508115801561167f575080155b1561169d5760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b031916600117855583156116c657845460ff60401b1916600160401b1785555b6001600160a01b0387166116ed5760405163d92e233d60e01b815260040160405180910390fd5b6116f63361220a565b6117026004601461221b565b61170b86610844565b6117136111a3565b6001600160a01b0316876001600160a01b0316146117345761173487611feb565b831561177a57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff1660038111156117a8576117a8613674565b036117c657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156117de576117de613674565b146117fc57604051631860f69960e31b815260040160405180910390fd5b8060030154421161182057604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061190c578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b1580156118ea57600080fd5b505af11580156118fe573d6000803e3d6000fd5b506000979650505050505050565b815460ff191660021782556006820154600b83018190556000816001600160401b0381111561193d5761193d613648565b604051908082528060200260200182016040528015611966578160200160208202803683370190505b50905060005b828110156119db5784600901600086600601838154811061198f5761198f61365e565b60009182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106119c8576119c861365e565b602090810291909101015260010161196c565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b158015611a2257600080fd5b505af1158015611a36573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e58560060183604051610c88929190613b59565b6000806111ae61225a565b611a82611fb7565b6001600160a01b038116611aa95760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff166003811115611b1857611b18613674565b03611b3657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611b4e57611b4e613674565b14611b6c57604051631860f69960e31b815260040160405180910390fd5b8060030154421115611b9157604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff1615611bc45760405163257309f160e11b815260040160405180910390fd5b611bcd33610c9c565b611bea5760405163149fbcfd60e11b815260040160405180910390fd5b611bf533838561227e565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff19166001179055909150611c719083908361245a565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b60008181526009602052604090205480611ce2576040516322e679e360e11b815260040160405180910390fd5b919050565b6000611cf560046014612666565b905090565b611d02611fb7565b6000611d0c61225a565b80546001600160a01b0319166001600160a01b0384169081178255909150611d326111a3565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020613dab8339815191528210611d8557600080fd5b825464ffffffffff600160281b90910481169082168111611da557600080fd5b8260005b81866001016000611dba84886126cc565b64ffffffffff168152602001908152602001600020819055506000816001611de29190613b6c565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611e175750611faf565b60018516600003611ee3576000611e3883611e33886001613b85565b6126cc565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e9a91600401613ba2565b602060405180830381865af4158015611eb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611edb91906136a3565b935050611f9b565b6000611ef483611e33600189613bd3565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611f5691600401613ba2565b602060405180830381865af4158015611f73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9791906136a3565b9350505b50647fffffffff600194851c169301611da9565b505050505050565b33611fc06111a3565b6001600160a01b031614611fe9573360405163118cdaa760e01b815260040161074291906134b7565b565b6000611ff561225a565b80546001600160a01b0319168155905061200e826126ea565b5050565b8154600160281b900464ffffffffff16600080516020613dab833981519152821061203c57600080fd5b825464ffffffffff9081169082161061205457600080fd5b61205f816001613b85565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b8185600101600061209684876126cc565b64ffffffffff16815260208101919091526040016000205560018316156121865760006120c882611e33600187613bd3565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161212a91600401613ba2565b602060405180830381865af4158015612147573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216b91906136a3565b647fffffffff600195861c1694909350919091019050612085565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000816040516020016121c49190613bf0565b604051602081830303815290604052805190602001209050919050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006106c5565b612212612746565b610eaa8161276b565b602060ff8216111561222c57600080fd5b61223d600160ff831681901b613c24565b82546001600160501b03191664ffffffffff919091161790915550565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b6000821161229f5760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166122c8576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd719188916122ff91613c24565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612348573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236c91906136a3565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123e791906136a3565b90506000811161240a5760405163aeaddff160e01b815260040160405180910390fd5b60006124168284613c37565b9050600081116124395760405163149fbcfd60e11b815260040160405180910390fd5b8086111561177a5760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff16908111156124da57508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff191682179055905061265f565b600080876009016000856000815481106124f6576124f661365e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b84548110156125825760008960090160008784815481106125435761254361365e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612579578092508193505b50600101612520565b5080861061259757600094505050505061265f565b600088600a0160008685815481106125b1576125b161365e565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156125ef576125ef613674565b0217905550868483815481106126075761260761365e565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff161161267757600080fd5b602060ff8316111561268857600080fd5b8254600160281b900464ffffffffff16806126a760ff85166002613d69565b64ffffffffff1610156126b957600080fd5b6126c484828561279d565b949350505050565b6000816126e060ff851663ffffffff613d83565b61265f9190613b85565b60006126f461218d565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b61274e612855565b611fe957604051631afcd79f60e31b815260040160405180910390fd5b612773612746565b6001600160a01b038116610ea1576000604051631e4fbdf760e01b815260040161074291906134b7565b6000602060ff831611156127b057600080fd5b8264ffffffffff166000036127cf576127c88261286f565b905061265f565b60006127dc836001613b6c565b60ff166001600160401b038111156127f6576127f6613648565b60405190808252806020026020018201604052801561281f578160200160208202803683370190505b50905061282e85858584612ec4565b808360ff16815181106128435761284361365e565b60200260200101519150509392505050565b600061285f6121e1565b54600160401b900460ff16919050565b60008160ff1660000361288457506000919050565b8160ff166001036128b657507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036128e857507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361291a57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361294c57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361297e57507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036129b057507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036129e257507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612a1457507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612a4657507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a03612a7857507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612aaa57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612adc57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612b0e57507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612b4057507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612b7257507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612ba457507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612bd657507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612c0857507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612c3a57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612c6c57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612c9e57507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612cd057507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612d0257507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612d3457507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612d6657507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612d9857507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612dca57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612dfc57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612e2e57507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612e6057507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612e9257507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361022557507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff83161115612ed557600080fd5b60008364ffffffffff1611612ee957600080fd5b6000612ef6600185613bd3565b905060018116600003612f4e57846001016000612f146000846126cc565b64ffffffffff1681526020019081526020016000205482600081518110612f3d57612f3d61365e565b602002602001018181525050612f78565b612f58600061286f565b82600081518110612f6b57612f6b61365e565b6020026020010181815250505b60005b8360ff168160ff161015611faf57600182166000036130745773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612fce57612fce61365e565b60200260200101518152602001612fe48561286f565b8152506040518263ffffffff1660e01b81526004016130039190613ba2565b602060405180830381865af4158015613020573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061304491906136a3565b83613050836001613b6c565b60ff16815181106130635761306361365e565b60200260200101818152505061322e565b6000613081826001613b6c565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156131265760008760010160006130da8560016130c99190613b6c565b60018864ffffffffff16901c6126cc565b64ffffffffff16815260200190815260200160002054905080858460016131019190613b6c565b60ff16815181106131145761311461365e565b6020026020010181815250505061322c565b600087600101600061313f85600188611e339190613bd3565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106131975761319761365e565b60200260200101518152506040518263ffffffff1660e01b81526004016131be9190613ba2565b602060405180830381865af41580156131db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131ff91906136a3565b8561320b856001613b6c565b60ff168151811061321e5761321e61365e565b602002602001018181525050505b505b647fffffffff600192831c169101612f7b565b6001830191839082156132d35791602002820160005b838211156132a157833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613257565b80156132d15782816101000a81549063ffffffff02191690556004016020816003010492830192600103026132a1565b505b506132df9291506132e3565b5090565b5b808211156132df57600081556001016132e4565b60006020828403121561330a57600080fd5b81356001600160e01b03198116811461265f57600080fd5b6001600160a01b0381168114610eaa57600080fd5b60006020828403121561334957600080fd5b813561265f81613322565b60006020828403121561336657600080fd5b5035919050565b600081518084526020840193506020830160005b828110156133a85781516001600160a01b0316865260209586019590910190600101613381565b5093949350505050565b600081518084526020840193506020830160005b828110156133a85781518652602095860195909101906001016133c6565b6040815260006133f7604083018561336d565b828103602084015261340981856133b2565b95945050505050565b60008060006080848603121561342757600080fd5b83359250602084013591506080840185101561344257600080fd5b6040840190509250925092565b6000806040838503121561346257600080fd5b82359150602083013561347481613322565b809150509250929050565b60008060006060848603121561349457600080fd5b8335925060208401356134a681613322565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b60208152600061265f602083018461336d565b60008083601f8401126134f057600080fd5b5081356001600160401b0381111561350757600080fd5b60208301915083602082850101111561351f57600080fd5b9250929050565b6000806000806000806080878903121561353f57600080fd5b8635955060208701356001600160401b0381111561355c57600080fd5b61356889828a016134de565b9096509450506040870135925060608701356001600160401b0381111561358e57600080fd5b61359a89828a016134de565b979a9699509497509295939492505050565b600080604083850312156135bf57600080fd5b82356135ca81613322565b946020939093013593505050565b600080604083850312156135eb57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b60008161361f5761361f6135fa565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b60006001820161369c5761369c6135fa565b5060010190565b6000602082840312156136b557600080fd5b5051919050565b803563ffffffff81168114611ce257600080fd5b6000602082840312156136e257600080fd5b61265f826136bc565b808201808211156106c5576106c56135fa565b84815260a08101602082018560005b600281101561373a5763ffffffff613724836136bc565b168352602092830192919091019060010161370d565b50505060608201939093526080015292915050565b80518015158114611ce257600080fd5b60006020828403121561377157600080fd5b61265f8261374f565b6040516101e081016001600160401b038111828210171561379d5761379d613648565b60405290565b604051601f8201601f191681016001600160401b03811182821017156137cb576137cb613648565b604052919050565b805160048110611ce257600080fd5b600082601f8301126137f357600080fd5b604080519081016001600160401b038111828210171561381557613815613648565b806040525080604084018581111561382c57600080fd5b845b8181101561384657805183526020928301920161382e565b509195945050505050565b8051611ce281613322565b805160ff81168114611ce257600080fd5b600082601f83011261387e57600080fd5b81516001600160401b0381111561389757613897613648565b6138aa601f8201601f19166020016137a3565b8181528460208386010111156138bf57600080fd5b60005b828110156138de576020818601810151838301820152016138c2565b506000918101602001919091529392505050565b60006020828403121561390457600080fd5b81516001600160401b0381111561391a57600080fd5b8201610200818503121561392d57600080fd5b61393561377a565b81518152613945602083016137d3565b60208201526040828101519082015261396185606084016137e2565b606082015260a0820151608082015261397c60c08301613851565b60a082015261398d60e0830161385c565b60c08201526101008201516001600160401b038111156139ac57600080fd5b6139b88682850161386d565b60e0830152506139cb6101208301613851565b6101008201526139de6101408301613851565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613a1557600080fd5b613a218682850161386d565b61018083015250613a356101c08301613851565b6101a0820152613a486101e0830161374f565b6101c0820152949350505050565b6000815480845260208401935082600052602060002060005b828110156133a85781546001600160a01b0316865260209095019460019182019101613a6f565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b87815286602082015260c060408201526000613ade60c0830188613a56565b86606084015285608084015282810360a0840152613afd818587613a96565b9a9950505050505050505050565b608081526000613b1e6080830189613a56565b8281036020840152613b3181888a613a96565b90508560408401528281036060840152613b4c818587613a96565b9998505050505050505050565b6040815260006133f76040830185613a56565b60ff81811683821601908111156106c5576106c56135fa565b64ffffffffff81811683821601908111156106c5576106c56135fa565b60408101818360005b6002811015613bca578151835260209283019290910190600101613bab565b50505092915050565b64ffffffffff82811682821603908111156106c5576106c56135fa565b8151600090829060208501835b828110156138465781516001600160a01b0316845260209384019390910190600101613bfd565b818103818111156106c5576106c56135fa565b600082613c5457634e487b7160e01b600052601260045260246000fd5b500490565b6001815b600184111561105057808504811115613c7857613c786135fa565b6001841615613c8657908102905b60019390931c928002613c5d565b600082613ca3575060016106c5565b81613cb0575060006106c5565b8160018114613cc65760028114613cd057613d02565b60019150506106c5565b60ff841115613ce157613ce16135fa565b6001841b915064ffffffffff821115613cfc57613cfc6135fa565b506106c5565b5060208310610133831016604e8410600b8410161715613d3a575081810a64ffffffffff811115613d3557613d356135fa565b6106c5565b613d4a64ffffffffff8484613c59565b8064ffffffffff04821115613d6157613d616135fa565b029392505050565b600061265f64ffffffffff841664ffffffffff8416613c94565b64ffffffffff8181168382160290811690818114613da357613da36135fa565b509291505056fe30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "bytecode": "0x6080604052348015600f57600080fd5b506016601a565b60ca565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560695760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b614cd7806100d96000396000f3fe608060405234801561001057600080fd5b50600436106102f65760003560e01c806301ffc9a7146102fb578063096b810a14610323578063099a161a146103385780630b45f8e9146103595780630b88a79a1461036c5780630bbfade7146103755780630f3e34121461038857806317d611201461039b5780631e08d0e8146103bc5780632800d829146103c4578063291a691b146103d75780632e7b716d146103ea578063323beaa5146103fd5780634d6861a61461041057806350e6d94c1461042357806350e6fad3146104465780635d504776146104505780635efb633c1461046357806362cc89a81461046c578063638a9d181461048c57806366d3da80146104955780636c120a951461049d57806370e36bbe146104b0578063715018a6146104c357806379ba5097146104cb5780637c92f524146104d357806385814243146105005780638a78bb15146105135780638cb89ecb146105265780638d1ddfb1146105465780638da5cb5b1461055c5780638e5ce3ad146105645780639015d3711461057757806392c0118f1461058a578063967966cc1461059d5780639a7a2ffc146105b05780639f0f874a146105ed578063a0164930146105f6578063a8a4d69b14610616578063acc5249414610629578063b0101d381461063c578063b2d5d1ac14610645578063b8ab47041461064e578063bbe4b80314610670578063bff232c11461067a578063c2b40ae41461068d578063c3a0ec30146106ad578063c8fe182d146106be578063ca2869a0146106c6578063cd6dc687146106e6578063ce82b84014610446578063cf90b6ed146106f9578063da881e5a14610703578063dbb06c9314610716578063e30c397814610729578063e4be6e3d14610731578063e4d185db14610744578063e59e469514610757578063e6745e131461076a578063e82f3b701461077d578063ebf0c71714610790578063f165053614610798578063f2fde38b146107b2578063f379b0df146107c5578063f52fd803146107ff578063f6fc05d514610870575b600080fd5b61030e610309366004613f75565b610879565b60405190151581526020015b60405180910390f35b610336610331366004613fb4565b6108b0565b005b61034b610346366004613fd1565b6109ef565b60405190815260200161031a565b610336610367366004613fb4565b610a29565b61034b61070881565b610336610383366004614032565b610b0c565b610336610396366004613fd1565b610dcd565b6103ae6103a9366004613fd1565b610e48565b60405161031a92919061415f565b61034b600181565b61034b6103d2366004613fd1565b610ff8565b61030e6103e536600461418d565b611045565b61030e6103f8366004613fb4565b611226565b61033661040b366004613fb4565b6112d7565b61030e61041e366004613fd1565b6113d1565b61030e610431366004613fb4565b60066020526000908152604090205460ff1681565b61034b6202a30081565b61030e61045e3660046141ca565b611412565b61034b600f5481565b600d5461047f906001600160a01b031681565b60405161031a91906141fa565b61034b60115481565b610336611457565b6103366104ab366004613fd1565b6114cb565b6103366104be366004613fb4565b611517565b61033661158e565b6103366115b2565b6104e66104e136600461420e565b6115f1565b6040805192835263ffffffff90911660208301520161031a565b60015461047f906001600160a01b031681565b610336610521366004613fb4565b611793565b61034b610534366004613fd1565b60096020526000908152604090205481565b600454600160281b900464ffffffffff1661034b565b61047f6118de565b600b5461047f906001600160a01b031681565b61030e610585366004613fb4565b6118f9565b610336610598366004613fd1565b611917565b6103366105ab366004613fd1565b6119dc565b6105d76105be366004613fb4565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161031a565b61034b60035481565b610609610604366004613fd1565b611a2f565b60405161031a9190614246565b61030e6106243660046141ca565b611ac8565b610336610637366004613fb4565b611b0d565b61034b60105481565b61034b600e5481565b61066161065c366004613fd1565b611ba4565b60405161031a93929190614259565b61034b6210000081565b610336610688366004613fb4565b611cf7565b61034b61069b366004613fd1565b60086020526000908152604090205481565b6001546001600160a01b031661047f565b610336611d70565b61034b6106d4366004613fd1565b60009081526008602052604090205490565b6103366106f436600461429c565b611ddb565b61034b62093a8081565b61030e610711366004613fd1565b611f63565b60005461047f906001600160a01b031681565b61047f612258565b600c5461047f906001600160a01b031681565b61047f6107523660046142c8565b612263565b610336610765366004613fb4565b612304565b6103366107783660046142c8565b61237d565b61034b61078b366004613fd1565b612546565b61034b612578565b6107a0601481565b60405160ff909116815260200161031a565b6103366107c0366004613fb4565b61258b565b6004546107e19064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161031a565b61084161080d366004613fd1565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161031a949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61034b60025481565b60006001600160e01b03198216630aa0697960e01b14806108aa57506001600160e01b031982166301ffc9a760e01b145b92915050565b6108b86118de565b6001600160a01b0316336001600160a01b031614806108e157506001546001600160a01b031633145b6108fe57604051632864c4e160e01b815260040160405180910390fd5b610907816118f9565b8190610930576040516381e5828960e01b815260040161092791906141fa565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff169061095f90600490836125fc565b6001600160a01b0382166000908152600660205260408120805460ff19169055600280549161098d83614300565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5926109e392869291600160281b900464ffffffffff1690614317565b60405180910390a25050565b6000818152600a602052604081206004810154610a1f576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b610a31612848565b600c546001600160a01b031615610a5a5760405162035f5560e61b815260040160405180910390fd5b6001600160a01b038116610a815760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d541615610ae757600d80546001600160a01b031981169091556000600e8190556040516001600160a01b03909216918291600080516020614c4b83398151915291a2505b6040516001600160a01b03821690600080516020614c6b83398151915290600090a250565b6000888152600a602052604090206002815460ff166003811115610b3257610b32614338565b14610b5057604051634f4b461f60e11b815260040160405180910390fd5b600481015415610b735760405163632a22bb60e01b815260040160405180910390fd5b85610b9157604051636caad1ed60e11b815260040160405180910390fd5b6000610bf882600601805480602002602001604051908101604052809291908181526020018280548015610bee57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610bd0575b505050505061287c565b60078301819055600480840189905560008c8152600960205260408082208b90558154905163101bb4d760e21b81529283018e9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015610c61573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c8991908101906144ec565b9050806101c0015115610d1757610d178b82600860008f81526020019081526020016000205486600601805480602002602001604051908101604052809291908181526020018280548015610d0757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610ce9575b50505050508c878d8d8d8d612935565b6000546040516340a3b76160e11b81526001600160a01b03909116906381476ec290610d49908e908c90600401614650565b600060405180830381600087803b158015610d6357600080fd5b505af1158015610d77573d6000803e3d6000fd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610db8969594939291906146c7565b60405180910390a25050505050505050505050565b610dd5612848565b60018110158015610de9575062093a808111155b8190610e0b5760405163028237cd60e61b815260040161092791815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab7906020015b60405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610e7f57610e7f61434e565b604051908082528060200260200182016040528015610ea8578160200160208202803683370190505b509450806001600160401b03811115610ec357610ec361434e565b604051908082528060200260200182016040528015610eec578160200160208202803683370190505b5093506000805b83811015610fee576000856006018281548110610f1257610f12614715565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff166002811115610f5a57610f5a614338565b03610fe55780888481518110610f7257610f72614715565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610fcc57610fcc614715565b602090810291909101015282610fe18161472b565b9350505b50600101610ef3565b5050505050915091565b6000818152600a6020526040812081815460ff16600381111561101d5761101d614338565b0361103b57604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b031633146110715760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff16600381111561109657611096614338565b146110b4576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa1580156110fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111229190614744565b9050806111356040860160208701614771565b63ffffffff16111561114d6040860160208701614771565b82909161117b576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610927565b5050815460ff191660019081178355820185905542600283018190556003546111a39161478c565b60038301556111b760058301856002613e83565b506111c0612578565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092611212928a928a929161479f565b60405180910390a250600195945050505050565b6000611231826118f9565b61123d57506000919050565b6001546001600160a01b0316611266576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d7906112969085906004016141fa565b602060405180830381865afa1580156112b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108aa91906147f0565b6112df612848565b600d546001600160a01b0316806113095760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461134a57604051630d77758160e31b81526001600160a01b03928316600482015291166024820152604401610927565b505060006202a300600e5461135f919061478c565b9050804281811015611386576040516337c8270b60e01b8152600401610927929190614650565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690556000600e819055604051600080516020614c6b8339815191529190a2505050565b6000818152600a602052604081206001815460ff1660038111156113f7576113f7614338565b146114055750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561144f5761144f614338565b149392505050565b61145f612848565b6011546000819003611484576040516374c28a4f60e11b815260040160405180910390fd5b601080546000918290556011919091556040518181527f1b445bf433fa24e3bc86b4be0682e0f89cb10813cc447a34c3480bfb32700dce9060200160405180910390a15050565b6114d3612848565b806000036114f457604051630823cf3d60e41b815260040160405180910390fd5b600f819055604051818152600080516020614c8b83398151915290602001610e3d565b61151f612848565b6001600160a01b0381166115465760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b611596612848565b6040516001623f026d60e01b0319815260040160405180910390fd5b33806115bc612258565b6001600160a01b0316146115e5578060405163118cdaa760e01b815260040161092791906141fa565b6115ee816129eb565b50565b600b5460009081906001600160a01b031633146116215760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff16600381111561164757611647614338565b1461166557604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff1660028111156116a6576116a6614338565b146116b657600b0154915061178b565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b82018054916116eb83614300565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051611733929190614650565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b61179b6118de565b6001600160a01b0316336001600160a01b031614806117c457506001546001600160a01b031633145b6117e157604051632864c4e160e01b815260040160405180910390fd5b6117ea816118f9565b6115ee57600454600160281b900464ffffffffff16621000008110611822576040516335b4ac3f60e01b815260040160405180910390fd5b61183660046001600160a01b038416612a12565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916118888361472b565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53926109e392869291600160281b900464ffffffffff1690614317565b6000806118e9612b8d565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b61191f612848565b6011546000819003611944576040516374c28a4f60e11b815260040160405180910390fd5b601054808380821461196b57604051632031a2b360e21b8152600401610927929190614650565b506000905061197d6202a3008461478c565b90508042818110156119a457604051630cdb45f960e11b8152600401610927929190614650565b5050600f84905560006010819055601155604051848152600080516020614c8b8339815191529060200160405180910390a150505050565b6119e4612848565b60108190554260118190557f0f002333a4ea310bce79580413099a161d9f7738865c31e2b05fd6e6d869bd55908290611a21906202a3009061478c565b604051610e3d929190614650565b6000818152600a60205260409020600481015460609190611a63576040516322e679e360e11b815260040160405180910390fd5b80600601805480602002602001604051908101604052809291908181526020018280548015611abb57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611a9d575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115611b0457611b04614338565b14159392505050565b611b15612848565b6001600160a01b038116611b3c5760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f2590611b90906202a3009061478c565b60405190815260200160405180910390a250565b60008181526009602052604090205460609081908190611bd7576040516322e679e360e11b815260040160405180910390fd5b600084815260126020908152604080832060138352818420601484529382902081548351818602810186019094528084529194939092918591830182828015611c3f57602002820191906000526020600020905b815481526020019060010190808311611c2b575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015611c9157602002820191906000526020600020905b815481526020019060010190808311611c7d575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015611ce357602002820191906000526020600020905b815481526020019060010190808311611ccf575b505050505090509250925092509193909250565b611cff612848565b6001600160a01b038116611d265760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b611d78612848565b600d546001600160a01b031680611da25760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690556000600e8190556040516001600160a01b03831691600080516020614c4b83398151915291a250565b6000611de5612bb1565b805490915060ff600160401b82041615906001600160401b0316600081158015611e0c5750825b90506000826001600160401b03166001148015611e285750303b155b905081158015611e36575080155b15611e545760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315611e7d57845460ff60401b1916600160401b1785555b6001600160a01b038716611ea45760405163d92e233d60e01b815260040160405180910390fd5b611ead33612bda565b611eb960046014612beb565b611ec286610dcd565b610708600f819055604051908152600080516020614c8b8339815191529060200160405180910390a1611ef36118de565b6001600160a01b0316876001600160a01b031614611f1457611f14876129eb565b8315611f5a57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff166003811115611f8857611f88614338565b03611fa657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611fbe57611fbe614338565b14611fdc57604051631860f69960e31b815260040160405180910390fd5b8060030154421161200057604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff161115806120ec578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b1580156120ca57600080fd5b505af11580156120de573d6000803e3d6000fd5b506000979650505050505050565b6120f582612c2a565b815460ff191660021782556006820154600b83018190556000816001600160401b038111156121265761212661434e565b60405190808252806020026020018201604052801561214f578160200160208202803683370190505b50905060005b828110156121c45784600901600086600601838154811061217857612178614715565b60009182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106121b1576121b1614715565b6020908102919091010152600101612155565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b15801561220b57600080fd5b505af115801561221f573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e5856006018360405161121292919061480b565b6000806118e9612d5b565b6000828152600a602052604081206002815460ff16600381111561228957612289614338565b146122a757604051634f4b461f60e11b815260040160405180910390fd5b600681015483908082106122d0576040516326c5c55b60e11b8152600401610927929190614650565b50508060060183815481106122e7576122e7614715565b6000918252602090912001546001600160a01b0316949350505050565b61230c612848565b6001600160a01b0381166123335760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff1660038111156123a2576123a2614338565b036123c057604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156123d8576123d8614338565b146123f657604051631860f69960e31b815260040160405180910390fd5b806003015442111561241b57604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff161561244e5760405163257309f160e11b815260040160405180910390fd5b61245733611226565b6124745760405163149fbcfd60e11b815260040160405180910390fd5b61247f338385612d7f565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff191660011790559091506124fb90839083612f5b565b50336001600160a01b0316847f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd8584604051612538929190614650565b60405180910390a350505050565b60008181526009602052604090205480612573576040516322e679e360e11b815260040160405180910390fd5b919050565b600061258660046014613167565b905090565b612593612848565b600061259d612d5b565b80546001600160a01b0319166001600160a01b03841690811782559091506125c36118de565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020614cab833981519152821061261657600080fd5b825464ffffffffff600160281b9091048116908216811161263657600080fd5b8260005b8186600101600061264b84886131cd565b64ffffffffff168152602001908152602001600020819055506000816001612673919061481e565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116126a85750612840565b600185166000036127745760006126c9836126c4886001614837565b6131cd565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161272b91600401614854565b602060405180830381865af4158015612748573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061276c9190614744565b93505061282c565b6000612785836126c4600189614885565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916127e791600401614854565b602060405180830381865af4158015612804573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128289190614744565b9350505b50647fffffffff600194851c16930161263a565b505050505050565b336128516118de565b6001600160a01b03161461287a573360405163118cdaa760e01b815260040161092791906141fa565b565b80516000908161288d8260146148a2565b6001600160401b038111156128a4576128a461434e565b6040519080825280601f01601f1916602001820160405280156128ce576020820181803683370190505b50905060005b828110156129255760008582815181106128f0576128f0614715565b602002602001015160601b9050600082601461290c91906148a2565b60609290921b91840160200191909152506001016128d4565b5080516020909101209392505050565b8261295357604051630fb0193f60e41b815260040160405180910390fd5b8861012001516001600160a01b0316630741a9da8b8a8a8a8a8a8a6040518863ffffffff1660e01b815260040161299097969594939291906148b9565b602060405180830381865afa1580156129ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129d191906147f0565b506129df8a858585856131eb565b50505050505050505050565b60006129f5612d5b565b80546001600160a01b03191681559050612a0e8261332c565b5050565b8154600160281b900464ffffffffff16600080516020614cab8339815191528210612a3c57600080fd5b825464ffffffffff90811690821610612a5457600080fd5b612a5f816001614837565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b81856001016000612a9684876131cd565b64ffffffffff1681526020810191909152604001600020556001831615612b86576000612ac8826126c4600187614885565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612b2a91600401614854565b602060405180830381865af4158015612b47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6b9190614744565b647fffffffff600195861c1694909350919091019050612a85565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006108aa565b612be2613388565b6115ee816133ad565b602060ff82161115612bfc57600080fd5b612c0d600160ff831681901b614905565b82546001600160501b03191664ffffffffff919091161790915550565b600681015460005b81811015612d56576000612c4782600161478c565b90505b82811015612d4d576000846006018381548110612c6957612c69614715565b60009182526020822001546006870180546001600160a01b0390921693509084908110612c9857612c98614715565b6000918252602090912001546001600160a01b0390811691508216811015612d435780866006018581548110612cd057612cd0614715565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612d1457612d14614715565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612c4a565b50600101612c32565b505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b60008211612da05760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612dc9576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612e0091614905565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612e49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e6d9190614744565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ec4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ee89190614744565b905060008111612f0b5760405163aeaddff160e01b815260040160405180910390fd5b6000612f178284614918565b905060008111612f3a5760405163149fbcfd60e11b815260040160405180910390fd5b80861115611f5a5760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff1690811115612fdb57508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050613160565b60008087600901600085600081548110612ff757612ff7614715565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b845481101561308357600089600901600087848154811061304457613044614715565b60009182526020808320909101546001600160a01b0316835282019290925260400190205490508281111561307a578092508193505b50600101613021565b50808610613098576000945050505050613160565b600088600a0160008685815481106130b2576130b2614715565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156130f0576130f0614338565b02179055508684838154811061310857613108614715565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff161161317857600080fd5b602060ff8316111561318957600080fd5b8254600160281b900464ffffffffff16806131a860ff85166002614a4a565b64ffffffffff1610156131ba57600080fd5b6131c58482856133df565b949350505050565b6000816131e160ff851663ffffffff614a64565b6131609190614837565b8061320957604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661323257604051630d6eeecb60e01b815260040160405180910390fd5b600c54604051635a3b618f60e11b8152600091829182916001600160a01b03169063b476c31e9061327390309046908d908d908d908d908d90600401614a8b565b600060405180830381865afa158015613290573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132b89190810190614b5c565b60008b815260126020908152604090912084519497509295509093506132e19290860190613f25565b506000888152601360209081526040909120835161330192850190613f25565b506000888152601460209081526040909120825161332192840190613f25565b505050505050505050565b6000613336612b8d565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b613390613497565b61287a57604051631afcd79f60e31b815260040160405180910390fd5b6133b5613388565b6001600160a01b0381166115e5576000604051631e4fbdf760e01b815260040161092791906141fa565b6000602060ff831611156133f257600080fd5b8264ffffffffff166000036134115761340a826134b1565b9050613160565b600061341e83600161481e565b60ff166001600160401b038111156134385761343861434e565b604051908082528060200260200182016040528015613461578160200160208202803683370190505b50905061347085858584613b06565b808360ff168151811061348557613485614715565b60200260200101519150509392505050565b60006134a1612bb1565b54600160401b900460ff16919050565b60008160ff166000036134c657506000919050565b8160ff166001036134f857507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361352a57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361355c57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361358e57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036135c057507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036135f257507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361362457507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361365657507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361368857507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036136ba57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036136ec57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361371e57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361375057507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361378257507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f036137b457507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff166010036137e657507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361381857507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361384a57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361387c57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036138ae57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff166015036138e057507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361391257507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361394457507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361397657507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff166019036139a857507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a036139da57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03613a0c57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03613a3e57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03613a7057507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03613aa257507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03613ad457507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036102f657507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff83161115613b1757600080fd5b60008364ffffffffff1611613b2b57600080fd5b6000613b38600185614885565b905060018116600003613b9057846001016000613b566000846131cd565b64ffffffffff1681526020019081526020016000205482600081518110613b7f57613b7f614715565b602002602001018181525050613bba565b613b9a60006134b1565b82600081518110613bad57613bad614715565b6020026020010181815250505b60005b8360ff168160ff1610156128405760018216600003613cb65773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110613c1057613c10614715565b60200260200101518152602001613c26856134b1565b8152506040518263ffffffff1660e01b8152600401613c459190614854565b602060405180830381865af4158015613c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c869190614744565b83613c9283600161481e565b60ff1681518110613ca557613ca5614715565b602002602001018181525050613e70565b6000613cc382600161481e565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613d68576000876001016000613d1c856001613d0b919061481e565b60018864ffffffffff16901c6131cd565b64ffffffffff1681526020019081526020016000205490508085846001613d43919061481e565b60ff1681518110613d5657613d56614715565b60200260200101818152505050613e6e565b6000876001016000613d81856001886126c49190614885565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613dd957613dd9614715565b60200260200101518152506040518263ffffffff1660e01b8152600401613e009190614854565b602060405180830381865af4158015613e1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e419190614744565b85613e4d85600161481e565b60ff1681518110613e6057613e60614715565b602002602001018181525050505b505b647fffffffff600192831c169101613bbd565b600183019183908215613f155791602002820160005b83821115613ee357833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613e99565b8015613f135782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613ee3565b505b50613f21929150613f60565b5090565b828054828255906000526020600020908101928215613f15579160200282015b82811115613f15578251825591602001919060010190613f45565b5b80821115613f215760008155600101613f61565b600060208284031215613f8757600080fd5b81356001600160e01b03198116811461316057600080fd5b6001600160a01b03811681146115ee57600080fd5b600060208284031215613fc657600080fd5b813561316081613f9f565b600060208284031215613fe357600080fd5b5035919050565b60008083601f840112613ffc57600080fd5b5081356001600160401b0381111561401357600080fd5b60208301915083602082850101111561402b57600080fd5b9250929050565b60008060008060008060008060a0898b03121561404e57600080fd5b8835975060208901356001600160401b0381111561406b57600080fd5b6140778b828c01613fea565b9098509650506040890135945060608901356001600160401b0381111561409d57600080fd5b6140a98b828c01613fea565b90955093505060808901356001600160401b038111156140c857600080fd5b6140d48b828c01613fea565b999c989b5096995094979396929594505050565b600081518084526020840193506020830160005b828110156141235781516001600160a01b03168652602095860195909101906001016140fc565b5093949350505050565b600081518084526020840193506020830160005b82811015614123578151865260209586019590910190600101614141565b60408152600061417260408301856140e8565b8281036020840152614184818561412d565b95945050505050565b6000806000608084860312156141a257600080fd5b8335925060208401359150608084018510156141bd57600080fd5b6040840190509250925092565b600080604083850312156141dd57600080fd5b8235915060208301356141ef81613f9f565b809150509250929050565b6001600160a01b0391909116815260200190565b60008060006060848603121561422357600080fd5b83359250602084013561423581613f9f565b929592945050506040919091013590565b60208152600061316060208301846140e8565b60608152600061426c606083018661412d565b828103602084015261427e818661412d565b90508281036040840152614292818561412d565b9695505050505050565b600080604083850312156142af57600080fd5b82356142ba81613f9f565b946020939093013593505050565b600080604083850312156142db57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b60008161430f5761430f6142ea565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040516101e081016001600160401b03811182821017156143875761438761434e565b60405290565b604051601f8201601f191681016001600160401b03811182821017156143b5576143b561434e565b604052919050565b80516004811061257357600080fd5b600082601f8301126143dd57600080fd5b604080519081016001600160401b03811182821017156143ff576143ff61434e565b806040525080604084018581111561441657600080fd5b845b81811015614430578051835260209283019201614418565b509195945050505050565b805161257381613f9f565b805160ff8116811461257357600080fd5b600082601f83011261446857600080fd5b81516001600160401b038111156144815761448161434e565b614494601f8201601f191660200161438d565b8181528460208386010111156144a957600080fd5b60005b828110156144c8576020818601810151838301820152016144ac565b506000918101602001919091529392505050565b8051801515811461257357600080fd5b6000602082840312156144fe57600080fd5b81516001600160401b0381111561451457600080fd5b8201610200818503121561452757600080fd5b61452f614364565b8151815261453f602083016143bd565b60208201526040828101519082015261455b85606084016143cc565b606082015260a0820151608082015261457660c0830161443b565b60a082015261458760e08301614446565b60c08201526101008201516001600160401b038111156145a657600080fd5b6145b286828501614457565b60e0830152506145c5610120830161443b565b6101008201526145d8610140830161443b565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561460f57600080fd5b61461b86828501614457565b6101808301525061462f6101c0830161443b565b6101a08201526146426101e083016144dc565b6101c0820152949350505050565b918252602082015260400190565b6000815480845260208401935082600052602060002060005b828110156141235781546001600160a01b0316865260209095019460019182019101614677565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6080815260006146da608083018961465e565b82810360208401526146ed81888a61469e565b9050856040840152828103606084015261470881858761469e565b9998505050505050505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161473d5761473d6142ea565b5060010190565b60006020828403121561475657600080fd5b5051919050565b803563ffffffff8116811461257357600080fd5b60006020828403121561478357600080fd5b6131608261475d565b808201808211156108aa576108aa6142ea565b84815260a08101602082018560005b60028110156147db5763ffffffff6147c58361475d565b16835260209283019291909101906001016147ae565b50505060608201939093526080015292915050565b60006020828403121561480257600080fd5b613160826144dc565b604081526000614172604083018561465e565b60ff81811683821601908111156108aa576108aa6142ea565b64ffffffffff81811683821601908111156108aa576108aa6142ea565b60408101818360005b600281101561487c57815183526020928301929091019060010161485d565b50505092915050565b64ffffffffff82811682821603908111156108aa576108aa6142ea565b80820281158282048414176108aa576108aa6142ea565b87815286602082015260c0604082015260006148d860c08301886140e8565b86606084015285608084015282810360a08401526148f781858761469e565b9a9950505050505050505050565b818103818111156108aa576108aa6142ea565b60008261493557634e487b7160e01b600052601260045260246000fd5b500490565b6001815b600184111561178b57808504811115614959576149596142ea565b600184161561496757908102905b60019390931c92800261493e565b600082614984575060016108aa565b81614991575060006108aa565b81600181146149a757600281146149b1576149e3565b60019150506108aa565b60ff8411156149c2576149c26142ea565b6001841b915064ffffffffff8211156149dd576149dd6142ea565b506108aa565b5060208310610133831016604e8410600b8410161715614a1b575081810a64ffffffffff811115614a1657614a166142ea565b6108aa565b614a2b64ffffffffff848461493a565b8064ffffffffff04821115614a4257614a426142ea565b029392505050565b600061316064ffffffffff841664ffffffffff8416614975565b64ffffffffff8181168382160290811690818114614a8457614a846142ea565b5092915050565b60018060a01b038816815286602082015285604082015260a060608201526000614ab960a08301868861469e565b82810360808401526148f781858761469e565b60006001600160401b03821115614ae557614ae561434e565b5060051b60200190565b600082601f830112614b0057600080fd5b8151614b13614b0e82614acc565b61438d565b8082825260208201915060208360051b860101925085831115614b3557600080fd5b602085015b83811015614b52578051835260209283019201614b3a565b5095945050505050565b600080600060608486031215614b7157600080fd5b83516001600160401b03811115614b8757600080fd5b8401601f81018613614b9857600080fd5b8051614ba6614b0e82614acc565b8082825260208201915060208360051b850101925088831115614bc857600080fd5b6020840193505b82841015614bea578351825260209384019390910190614bcf565b6020880151909650925050506001600160401b03811115614c0a57600080fd5b614c1686828701614aef565b604086015190935090506001600160401b03811115614c3457600080fd5b614c4086828701614aef565b915050925092509256fe70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1bdd568bd039595fd4624b89bd697f998c689807d04c17303be9d1417adbdc056f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102f65760003560e01c806301ffc9a7146102fb578063096b810a14610323578063099a161a146103385780630b45f8e9146103595780630b88a79a1461036c5780630bbfade7146103755780630f3e34121461038857806317d611201461039b5780631e08d0e8146103bc5780632800d829146103c4578063291a691b146103d75780632e7b716d146103ea578063323beaa5146103fd5780634d6861a61461041057806350e6d94c1461042357806350e6fad3146104465780635d504776146104505780635efb633c1461046357806362cc89a81461046c578063638a9d181461048c57806366d3da80146104955780636c120a951461049d57806370e36bbe146104b0578063715018a6146104c357806379ba5097146104cb5780637c92f524146104d357806385814243146105005780638a78bb15146105135780638cb89ecb146105265780638d1ddfb1146105465780638da5cb5b1461055c5780638e5ce3ad146105645780639015d3711461057757806392c0118f1461058a578063967966cc1461059d5780639a7a2ffc146105b05780639f0f874a146105ed578063a0164930146105f6578063a8a4d69b14610616578063acc5249414610629578063b0101d381461063c578063b2d5d1ac14610645578063b8ab47041461064e578063bbe4b80314610670578063bff232c11461067a578063c2b40ae41461068d578063c3a0ec30146106ad578063c8fe182d146106be578063ca2869a0146106c6578063cd6dc687146106e6578063ce82b84014610446578063cf90b6ed146106f9578063da881e5a14610703578063dbb06c9314610716578063e30c397814610729578063e4be6e3d14610731578063e4d185db14610744578063e59e469514610757578063e6745e131461076a578063e82f3b701461077d578063ebf0c71714610790578063f165053614610798578063f2fde38b146107b2578063f379b0df146107c5578063f52fd803146107ff578063f6fc05d514610870575b600080fd5b61030e610309366004613f75565b610879565b60405190151581526020015b60405180910390f35b610336610331366004613fb4565b6108b0565b005b61034b610346366004613fd1565b6109ef565b60405190815260200161031a565b610336610367366004613fb4565b610a29565b61034b61070881565b610336610383366004614032565b610b0c565b610336610396366004613fd1565b610dcd565b6103ae6103a9366004613fd1565b610e48565b60405161031a92919061415f565b61034b600181565b61034b6103d2366004613fd1565b610ff8565b61030e6103e536600461418d565b611045565b61030e6103f8366004613fb4565b611226565b61033661040b366004613fb4565b6112d7565b61030e61041e366004613fd1565b6113d1565b61030e610431366004613fb4565b60066020526000908152604090205460ff1681565b61034b6202a30081565b61030e61045e3660046141ca565b611412565b61034b600f5481565b600d5461047f906001600160a01b031681565b60405161031a91906141fa565b61034b60115481565b610336611457565b6103366104ab366004613fd1565b6114cb565b6103366104be366004613fb4565b611517565b61033661158e565b6103366115b2565b6104e66104e136600461420e565b6115f1565b6040805192835263ffffffff90911660208301520161031a565b60015461047f906001600160a01b031681565b610336610521366004613fb4565b611793565b61034b610534366004613fd1565b60096020526000908152604090205481565b600454600160281b900464ffffffffff1661034b565b61047f6118de565b600b5461047f906001600160a01b031681565b61030e610585366004613fb4565b6118f9565b610336610598366004613fd1565b611917565b6103366105ab366004613fd1565b6119dc565b6105d76105be366004613fb4565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161031a565b61034b60035481565b610609610604366004613fd1565b611a2f565b60405161031a9190614246565b61030e6106243660046141ca565b611ac8565b610336610637366004613fb4565b611b0d565b61034b60105481565b61034b600e5481565b61066161065c366004613fd1565b611ba4565b60405161031a93929190614259565b61034b6210000081565b610336610688366004613fb4565b611cf7565b61034b61069b366004613fd1565b60086020526000908152604090205481565b6001546001600160a01b031661047f565b610336611d70565b61034b6106d4366004613fd1565b60009081526008602052604090205490565b6103366106f436600461429c565b611ddb565b61034b62093a8081565b61030e610711366004613fd1565b611f63565b60005461047f906001600160a01b031681565b61047f612258565b600c5461047f906001600160a01b031681565b61047f6107523660046142c8565b612263565b610336610765366004613fb4565b612304565b6103366107783660046142c8565b61237d565b61034b61078b366004613fd1565b612546565b61034b612578565b6107a0601481565b60405160ff909116815260200161031a565b6103366107c0366004613fb4565b61258b565b6004546107e19064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161031a565b61084161080d366004613fd1565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161031a949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61034b60025481565b60006001600160e01b03198216630aa0697960e01b14806108aa57506001600160e01b031982166301ffc9a760e01b145b92915050565b6108b86118de565b6001600160a01b0316336001600160a01b031614806108e157506001546001600160a01b031633145b6108fe57604051632864c4e160e01b815260040160405180910390fd5b610907816118f9565b8190610930576040516381e5828960e01b815260040161092791906141fa565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff169061095f90600490836125fc565b6001600160a01b0382166000908152600660205260408120805460ff19169055600280549161098d83614300565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5926109e392869291600160281b900464ffffffffff1690614317565b60405180910390a25050565b6000818152600a602052604081206004810154610a1f576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b610a31612848565b600c546001600160a01b031615610a5a5760405162035f5560e61b815260040160405180910390fd5b6001600160a01b038116610a815760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d541615610ae757600d80546001600160a01b031981169091556000600e8190556040516001600160a01b03909216918291600080516020614c4b83398151915291a2505b6040516001600160a01b03821690600080516020614c6b83398151915290600090a250565b6000888152600a602052604090206002815460ff166003811115610b3257610b32614338565b14610b5057604051634f4b461f60e11b815260040160405180910390fd5b600481015415610b735760405163632a22bb60e01b815260040160405180910390fd5b85610b9157604051636caad1ed60e11b815260040160405180910390fd5b6000610bf882600601805480602002602001604051908101604052809291908181526020018280548015610bee57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610bd0575b505050505061287c565b60078301819055600480840189905560008c8152600960205260408082208b90558154905163101bb4d760e21b81529283018e9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015610c61573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c8991908101906144ec565b9050806101c0015115610d1757610d178b82600860008f81526020019081526020016000205486600601805480602002602001604051908101604052809291908181526020018280548015610d0757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610ce9575b50505050508c878d8d8d8d612935565b6000546040516340a3b76160e11b81526001600160a01b03909116906381476ec290610d49908e908c90600401614650565b600060405180830381600087803b158015610d6357600080fd5b505af1158015610d77573d6000803e3d6000fd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610db8969594939291906146c7565b60405180910390a25050505050505050505050565b610dd5612848565b60018110158015610de9575062093a808111155b8190610e0b5760405163028237cd60e61b815260040161092791815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab7906020015b60405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610e7f57610e7f61434e565b604051908082528060200260200182016040528015610ea8578160200160208202803683370190505b509450806001600160401b03811115610ec357610ec361434e565b604051908082528060200260200182016040528015610eec578160200160208202803683370190505b5093506000805b83811015610fee576000856006018281548110610f1257610f12614715565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff166002811115610f5a57610f5a614338565b03610fe55780888481518110610f7257610f72614715565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610fcc57610fcc614715565b602090810291909101015282610fe18161472b565b9350505b50600101610ef3565b5050505050915091565b6000818152600a6020526040812081815460ff16600381111561101d5761101d614338565b0361103b57604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b031633146110715760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff16600381111561109657611096614338565b146110b4576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa1580156110fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111229190614744565b9050806111356040860160208701614771565b63ffffffff16111561114d6040860160208701614771565b82909161117b576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610927565b5050815460ff191660019081178355820185905542600283018190556003546111a39161478c565b60038301556111b760058301856002613e83565b506111c0612578565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092611212928a928a929161479f565b60405180910390a250600195945050505050565b6000611231826118f9565b61123d57506000919050565b6001546001600160a01b0316611266576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d7906112969085906004016141fa565b602060405180830381865afa1580156112b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108aa91906147f0565b6112df612848565b600d546001600160a01b0316806113095760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461134a57604051630d77758160e31b81526001600160a01b03928316600482015291166024820152604401610927565b505060006202a300600e5461135f919061478c565b9050804281811015611386576040516337c8270b60e01b8152600401610927929190614650565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690556000600e819055604051600080516020614c6b8339815191529190a2505050565b6000818152600a602052604081206001815460ff1660038111156113f7576113f7614338565b146114055750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561144f5761144f614338565b149392505050565b61145f612848565b6011546000819003611484576040516374c28a4f60e11b815260040160405180910390fd5b601080546000918290556011919091556040518181527f1b445bf433fa24e3bc86b4be0682e0f89cb10813cc447a34c3480bfb32700dce9060200160405180910390a15050565b6114d3612848565b806000036114f457604051630823cf3d60e41b815260040160405180910390fd5b600f819055604051818152600080516020614c8b83398151915290602001610e3d565b61151f612848565b6001600160a01b0381166115465760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b611596612848565b6040516001623f026d60e01b0319815260040160405180910390fd5b33806115bc612258565b6001600160a01b0316146115e5578060405163118cdaa760e01b815260040161092791906141fa565b6115ee816129eb565b50565b600b5460009081906001600160a01b031633146116215760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff16600381111561164757611647614338565b1461166557604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff1660028111156116a6576116a6614338565b146116b657600b0154915061178b565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b82018054916116eb83614300565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051611733929190614650565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b61179b6118de565b6001600160a01b0316336001600160a01b031614806117c457506001546001600160a01b031633145b6117e157604051632864c4e160e01b815260040160405180910390fd5b6117ea816118f9565b6115ee57600454600160281b900464ffffffffff16621000008110611822576040516335b4ac3f60e01b815260040160405180910390fd5b61183660046001600160a01b038416612a12565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916118888361472b565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53926109e392869291600160281b900464ffffffffff1690614317565b6000806118e9612b8d565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b61191f612848565b6011546000819003611944576040516374c28a4f60e11b815260040160405180910390fd5b601054808380821461196b57604051632031a2b360e21b8152600401610927929190614650565b506000905061197d6202a3008461478c565b90508042818110156119a457604051630cdb45f960e11b8152600401610927929190614650565b5050600f84905560006010819055601155604051848152600080516020614c8b8339815191529060200160405180910390a150505050565b6119e4612848565b60108190554260118190557f0f002333a4ea310bce79580413099a161d9f7738865c31e2b05fd6e6d869bd55908290611a21906202a3009061478c565b604051610e3d929190614650565b6000818152600a60205260409020600481015460609190611a63576040516322e679e360e11b815260040160405180910390fd5b80600601805480602002602001604051908101604052809291908181526020018280548015611abb57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611a9d575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115611b0457611b04614338565b14159392505050565b611b15612848565b6001600160a01b038116611b3c5760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f2590611b90906202a3009061478c565b60405190815260200160405180910390a250565b60008181526009602052604090205460609081908190611bd7576040516322e679e360e11b815260040160405180910390fd5b600084815260126020908152604080832060138352818420601484529382902081548351818602810186019094528084529194939092918591830182828015611c3f57602002820191906000526020600020905b815481526020019060010190808311611c2b575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015611c9157602002820191906000526020600020905b815481526020019060010190808311611c7d575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015611ce357602002820191906000526020600020905b815481526020019060010190808311611ccf575b505050505090509250925092509193909250565b611cff612848565b6001600160a01b038116611d265760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b611d78612848565b600d546001600160a01b031680611da25760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690556000600e8190556040516001600160a01b03831691600080516020614c4b83398151915291a250565b6000611de5612bb1565b805490915060ff600160401b82041615906001600160401b0316600081158015611e0c5750825b90506000826001600160401b03166001148015611e285750303b155b905081158015611e36575080155b15611e545760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315611e7d57845460ff60401b1916600160401b1785555b6001600160a01b038716611ea45760405163d92e233d60e01b815260040160405180910390fd5b611ead33612bda565b611eb960046014612beb565b611ec286610dcd565b610708600f819055604051908152600080516020614c8b8339815191529060200160405180910390a1611ef36118de565b6001600160a01b0316876001600160a01b031614611f1457611f14876129eb565b8315611f5a57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff166003811115611f8857611f88614338565b03611fa657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611fbe57611fbe614338565b14611fdc57604051631860f69960e31b815260040160405180910390fd5b8060030154421161200057604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff161115806120ec578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b1580156120ca57600080fd5b505af11580156120de573d6000803e3d6000fd5b506000979650505050505050565b6120f582612c2a565b815460ff191660021782556006820154600b83018190556000816001600160401b038111156121265761212661434e565b60405190808252806020026020018201604052801561214f578160200160208202803683370190505b50905060005b828110156121c45784600901600086600601838154811061217857612178614715565b60009182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106121b1576121b1614715565b6020908102919091010152600101612155565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b15801561220b57600080fd5b505af115801561221f573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e5856006018360405161121292919061480b565b6000806118e9612d5b565b6000828152600a602052604081206002815460ff16600381111561228957612289614338565b146122a757604051634f4b461f60e11b815260040160405180910390fd5b600681015483908082106122d0576040516326c5c55b60e11b8152600401610927929190614650565b50508060060183815481106122e7576122e7614715565b6000918252602090912001546001600160a01b0316949350505050565b61230c612848565b6001600160a01b0381166123335760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff1660038111156123a2576123a2614338565b036123c057604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156123d8576123d8614338565b146123f657604051631860f69960e31b815260040160405180910390fd5b806003015442111561241b57604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff161561244e5760405163257309f160e11b815260040160405180910390fd5b61245733611226565b6124745760405163149fbcfd60e11b815260040160405180910390fd5b61247f338385612d7f565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff191660011790559091506124fb90839083612f5b565b50336001600160a01b0316847f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd8584604051612538929190614650565b60405180910390a350505050565b60008181526009602052604090205480612573576040516322e679e360e11b815260040160405180910390fd5b919050565b600061258660046014613167565b905090565b612593612848565b600061259d612d5b565b80546001600160a01b0319166001600160a01b03841690811782559091506125c36118de565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020614cab833981519152821061261657600080fd5b825464ffffffffff600160281b9091048116908216811161263657600080fd5b8260005b8186600101600061264b84886131cd565b64ffffffffff168152602001908152602001600020819055506000816001612673919061481e565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116126a85750612840565b600185166000036127745760006126c9836126c4886001614837565b6131cd565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161272b91600401614854565b602060405180830381865af4158015612748573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061276c9190614744565b93505061282c565b6000612785836126c4600189614885565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916127e791600401614854565b602060405180830381865af4158015612804573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128289190614744565b9350505b50647fffffffff600194851c16930161263a565b505050505050565b336128516118de565b6001600160a01b03161461287a573360405163118cdaa760e01b815260040161092791906141fa565b565b80516000908161288d8260146148a2565b6001600160401b038111156128a4576128a461434e565b6040519080825280601f01601f1916602001820160405280156128ce576020820181803683370190505b50905060005b828110156129255760008582815181106128f0576128f0614715565b602002602001015160601b9050600082601461290c91906148a2565b60609290921b91840160200191909152506001016128d4565b5080516020909101209392505050565b8261295357604051630fb0193f60e41b815260040160405180910390fd5b8861012001516001600160a01b0316630741a9da8b8a8a8a8a8a8a6040518863ffffffff1660e01b815260040161299097969594939291906148b9565b602060405180830381865afa1580156129ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129d191906147f0565b506129df8a858585856131eb565b50505050505050505050565b60006129f5612d5b565b80546001600160a01b03191681559050612a0e8261332c565b5050565b8154600160281b900464ffffffffff16600080516020614cab8339815191528210612a3c57600080fd5b825464ffffffffff90811690821610612a5457600080fd5b612a5f816001614837565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b81856001016000612a9684876131cd565b64ffffffffff1681526020810191909152604001600020556001831615612b86576000612ac8826126c4600187614885565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612b2a91600401614854565b602060405180830381865af4158015612b47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6b9190614744565b647fffffffff600195861c1694909350919091019050612a85565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006108aa565b612be2613388565b6115ee816133ad565b602060ff82161115612bfc57600080fd5b612c0d600160ff831681901b614905565b82546001600160501b03191664ffffffffff919091161790915550565b600681015460005b81811015612d56576000612c4782600161478c565b90505b82811015612d4d576000846006018381548110612c6957612c69614715565b60009182526020822001546006870180546001600160a01b0390921693509084908110612c9857612c98614715565b6000918252602090912001546001600160a01b0390811691508216811015612d435780866006018581548110612cd057612cd0614715565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612d1457612d14614715565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612c4a565b50600101612c32565b505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b60008211612da05760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612dc9576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612e0091614905565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612e49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e6d9190614744565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ec4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ee89190614744565b905060008111612f0b5760405163aeaddff160e01b815260040160405180910390fd5b6000612f178284614918565b905060008111612f3a5760405163149fbcfd60e11b815260040160405180910390fd5b80861115611f5a5760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff1690811115612fdb57508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050613160565b60008087600901600085600081548110612ff757612ff7614715565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b845481101561308357600089600901600087848154811061304457613044614715565b60009182526020808320909101546001600160a01b0316835282019290925260400190205490508281111561307a578092508193505b50600101613021565b50808610613098576000945050505050613160565b600088600a0160008685815481106130b2576130b2614715565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156130f0576130f0614338565b02179055508684838154811061310857613108614715565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff161161317857600080fd5b602060ff8316111561318957600080fd5b8254600160281b900464ffffffffff16806131a860ff85166002614a4a565b64ffffffffff1610156131ba57600080fd5b6131c58482856133df565b949350505050565b6000816131e160ff851663ffffffff614a64565b6131609190614837565b8061320957604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661323257604051630d6eeecb60e01b815260040160405180910390fd5b600c54604051635a3b618f60e11b8152600091829182916001600160a01b03169063b476c31e9061327390309046908d908d908d908d908d90600401614a8b565b600060405180830381865afa158015613290573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132b89190810190614b5c565b60008b815260126020908152604090912084519497509295509093506132e19290860190613f25565b506000888152601360209081526040909120835161330192850190613f25565b506000888152601460209081526040909120825161332192840190613f25565b505050505050505050565b6000613336612b8d565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b613390613497565b61287a57604051631afcd79f60e31b815260040160405180910390fd5b6133b5613388565b6001600160a01b0381166115e5576000604051631e4fbdf760e01b815260040161092791906141fa565b6000602060ff831611156133f257600080fd5b8264ffffffffff166000036134115761340a826134b1565b9050613160565b600061341e83600161481e565b60ff166001600160401b038111156134385761343861434e565b604051908082528060200260200182016040528015613461578160200160208202803683370190505b50905061347085858584613b06565b808360ff168151811061348557613485614715565b60200260200101519150509392505050565b60006134a1612bb1565b54600160401b900460ff16919050565b60008160ff166000036134c657506000919050565b8160ff166001036134f857507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361352a57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361355c57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361358e57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036135c057507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036135f257507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361362457507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361365657507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361368857507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036136ba57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036136ec57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361371e57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361375057507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361378257507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f036137b457507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff166010036137e657507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361381857507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361384a57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361387c57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036138ae57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff166015036138e057507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361391257507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361394457507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361397657507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff166019036139a857507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a036139da57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03613a0c57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03613a3e57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03613a7057507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03613aa257507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03613ad457507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036102f657507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff83161115613b1757600080fd5b60008364ffffffffff1611613b2b57600080fd5b6000613b38600185614885565b905060018116600003613b9057846001016000613b566000846131cd565b64ffffffffff1681526020019081526020016000205482600081518110613b7f57613b7f614715565b602002602001018181525050613bba565b613b9a60006134b1565b82600081518110613bad57613bad614715565b6020026020010181815250505b60005b8360ff168160ff1610156128405760018216600003613cb65773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110613c1057613c10614715565b60200260200101518152602001613c26856134b1565b8152506040518263ffffffff1660e01b8152600401613c459190614854565b602060405180830381865af4158015613c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c869190614744565b83613c9283600161481e565b60ff1681518110613ca557613ca5614715565b602002602001018181525050613e70565b6000613cc382600161481e565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613d68576000876001016000613d1c856001613d0b919061481e565b60018864ffffffffff16901c6131cd565b64ffffffffff1681526020019081526020016000205490508085846001613d43919061481e565b60ff1681518110613d5657613d56614715565b60200260200101818152505050613e6e565b6000876001016000613d81856001886126c49190614885565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613dd957613dd9614715565b60200260200101518152506040518263ffffffff1660e01b8152600401613e009190614854565b602060405180830381865af4158015613e1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e419190614744565b85613e4d85600161481e565b60ff1681518110613e6057613e60614715565b602002602001018181525050505b505b647fffffffff600192831c169101613bbd565b600183019183908215613f155791602002820160005b83821115613ee357833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613e99565b8015613f135782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613ee3565b505b50613f21929150613f60565b5090565b828054828255906000526020600020908101928215613f15579160200282015b82811115613f15578251825591602001919060010190613f45565b5b80821115613f215760008155600101613f61565b600060208284031215613f8757600080fd5b81356001600160e01b03198116811461316057600080fd5b6001600160a01b03811681146115ee57600080fd5b600060208284031215613fc657600080fd5b813561316081613f9f565b600060208284031215613fe357600080fd5b5035919050565b60008083601f840112613ffc57600080fd5b5081356001600160401b0381111561401357600080fd5b60208301915083602082850101111561402b57600080fd5b9250929050565b60008060008060008060008060a0898b03121561404e57600080fd5b8835975060208901356001600160401b0381111561406b57600080fd5b6140778b828c01613fea565b9098509650506040890135945060608901356001600160401b0381111561409d57600080fd5b6140a98b828c01613fea565b90955093505060808901356001600160401b038111156140c857600080fd5b6140d48b828c01613fea565b999c989b5096995094979396929594505050565b600081518084526020840193506020830160005b828110156141235781516001600160a01b03168652602095860195909101906001016140fc565b5093949350505050565b600081518084526020840193506020830160005b82811015614123578151865260209586019590910190600101614141565b60408152600061417260408301856140e8565b8281036020840152614184818561412d565b95945050505050565b6000806000608084860312156141a257600080fd5b8335925060208401359150608084018510156141bd57600080fd5b6040840190509250925092565b600080604083850312156141dd57600080fd5b8235915060208301356141ef81613f9f565b809150509250929050565b6001600160a01b0391909116815260200190565b60008060006060848603121561422357600080fd5b83359250602084013561423581613f9f565b929592945050506040919091013590565b60208152600061316060208301846140e8565b60608152600061426c606083018661412d565b828103602084015261427e818661412d565b90508281036040840152614292818561412d565b9695505050505050565b600080604083850312156142af57600080fd5b82356142ba81613f9f565b946020939093013593505050565b600080604083850312156142db57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b60008161430f5761430f6142ea565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040516101e081016001600160401b03811182821017156143875761438761434e565b60405290565b604051601f8201601f191681016001600160401b03811182821017156143b5576143b561434e565b604052919050565b80516004811061257357600080fd5b600082601f8301126143dd57600080fd5b604080519081016001600160401b03811182821017156143ff576143ff61434e565b806040525080604084018581111561441657600080fd5b845b81811015614430578051835260209283019201614418565b509195945050505050565b805161257381613f9f565b805160ff8116811461257357600080fd5b600082601f83011261446857600080fd5b81516001600160401b038111156144815761448161434e565b614494601f8201601f191660200161438d565b8181528460208386010111156144a957600080fd5b60005b828110156144c8576020818601810151838301820152016144ac565b506000918101602001919091529392505050565b8051801515811461257357600080fd5b6000602082840312156144fe57600080fd5b81516001600160401b0381111561451457600080fd5b8201610200818503121561452757600080fd5b61452f614364565b8151815261453f602083016143bd565b60208201526040828101519082015261455b85606084016143cc565b606082015260a0820151608082015261457660c0830161443b565b60a082015261458760e08301614446565b60c08201526101008201516001600160401b038111156145a657600080fd5b6145b286828501614457565b60e0830152506145c5610120830161443b565b6101008201526145d8610140830161443b565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561460f57600080fd5b61461b86828501614457565b6101808301525061462f6101c0830161443b565b6101a08201526146426101e083016144dc565b6101c0820152949350505050565b918252602082015260400190565b6000815480845260208401935082600052602060002060005b828110156141235781546001600160a01b0316865260209095019460019182019101614677565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6080815260006146da608083018961465e565b82810360208401526146ed81888a61469e565b9050856040840152828103606084015261470881858761469e565b9998505050505050505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161473d5761473d6142ea565b5060010190565b60006020828403121561475657600080fd5b5051919050565b803563ffffffff8116811461257357600080fd5b60006020828403121561478357600080fd5b6131608261475d565b808201808211156108aa576108aa6142ea565b84815260a08101602082018560005b60028110156147db5763ffffffff6147c58361475d565b16835260209283019291909101906001016147ae565b50505060608201939093526080015292915050565b60006020828403121561480257600080fd5b613160826144dc565b604081526000614172604083018561465e565b60ff81811683821601908111156108aa576108aa6142ea565b64ffffffffff81811683821601908111156108aa576108aa6142ea565b60408101818360005b600281101561487c57815183526020928301929091019060010161485d565b50505092915050565b64ffffffffff82811682821603908111156108aa576108aa6142ea565b80820281158282048414176108aa576108aa6142ea565b87815286602082015260c0604082015260006148d860c08301886140e8565b86606084015285608084015282810360a08401526148f781858761469e565b9a9950505050505050505050565b818103818111156108aa576108aa6142ea565b60008261493557634e487b7160e01b600052601260045260246000fd5b500490565b6001815b600184111561178b57808504811115614959576149596142ea565b600184161561496757908102905b60019390931c92800261493e565b600082614984575060016108aa565b81614991575060006108aa565b81600181146149a757600281146149b1576149e3565b60019150506108aa565b60ff8411156149c2576149c26142ea565b6001841b915064ffffffffff8211156149dd576149dd6142ea565b506108aa565b5060208310610133831016604e8410600b8410161715614a1b575081810a64ffffffffff811115614a1657614a166142ea565b6108aa565b614a2b64ffffffffff848461493a565b8064ffffffffff04821115614a4257614a426142ea565b029392505050565b600061316064ffffffffff841664ffffffffff8416614975565b64ffffffffff8181168382160290811690818114614a8457614a846142ea565b5092915050565b60018060a01b038816815286602082015285604082015260a060608201526000614ab960a08301868861469e565b82810360808401526148f781858761469e565b60006001600160401b03821115614ae557614ae561434e565b5060051b60200190565b600082601f830112614b0057600080fd5b8151614b13614b0e82614acc565b61438d565b8082825260208201915060208360051b860101925085831115614b3557600080fd5b602085015b83811015614b52578051835260209283019201614b3a565b5095945050505050565b600080600060608486031215614b7157600080fd5b83516001600160401b03811115614b8757600080fd5b8401601f81018613614b9857600080fd5b8051614ba6614b0e82614acc565b8082825260208201915060208360051b850101925088831115614bc857600080fd5b6020840193505b82841015614bea578351825260209384019390910190614bcf565b6020880151909650925050506001600160401b03811115614c0a57600080fd5b614c1686828701614aef565b604086015190935090506001600160401b03811115614c3457600080fd5b614c4086828701614aef565b915050925092509256fe70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1bdd568bd039595fd4624b89bd697f998c689807d04c17303be9d1417adbdc056f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", "linkReferences": { "npm/poseidon-solidity@0.0.5/PoseidonT3.sol": { "PoseidonT3": [ { "length": 20, - "start": 8013 + "start": 10206 }, { "length": 20, - "start": 8201 + "start": 10394 }, { "length": 20, - "start": 8669 + "start": 11229 }, { "length": 20, - "start": 12399 + "start": 15537 }, { "length": 20, - "start": 12850 + "start": 15988 } ] } @@ -1476,28 +1958,28 @@ "PoseidonT3": [ { "length": 20, - "start": 7796 + "start": 9989 }, { "length": 20, - "start": 7984 + "start": 10177 }, { "length": 20, - "start": 8452 + "start": 11012 }, { "length": 20, - "start": 12182 + "start": 15320 }, { "length": 20, - "start": 12633 + "start": 15771 } ] } }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" + "buildInfoId": "solc-0_8_28-834ec0837fe1c9299862cfab43bdd2eef32c6092" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json index c33f03dfab..a085967ec5 100644 --- a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json +++ b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json @@ -1428,7 +1428,7 @@ "linkReferences": {}, "deployedLinkReferences": {}, "immutableReferences": { - "4010": [ + "4593": [ { "length": 32, "start": 3120 @@ -1442,43 +1442,43 @@ "start": 5419 } ], - "7066": [ + "7931": [ { "length": 32, "start": 5651 } ], - "7068": [ + "7933": [ { "length": 32, "start": 5609 } ], - "7070": [ + "7935": [ { "length": 32, "start": 5567 } ], - "7072": [ + "7937": [ { "length": 32, "start": 5732 } ], - "7074": [ + "7939": [ { "length": 32, "start": 5772 } ], - "7077": [ + "7942": [ { "length": 32, "start": 6200 } ], - "7080": [ + "7945": [ { "length": 32, "start": 6245 @@ -1486,5 +1486,5 @@ ] }, "inputSourceName": "project/contracts/token/EnclaveTicketToken.sol", - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" + "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol index f646b71b26..0abbea7b2d 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -156,6 +156,20 @@ interface ICiphernodeRegistry { /// @param enclave Address of the enclave contract. event EnclaveSet(address indexed enclave); + /// @notice Emitted when the owner proposes a new DKG fold-attestation verifier. + /// @dev The new verifier becomes active only after `commitDkgFoldAttestationVerifier` + /// is called and at least `DKG_FOLD_VERIFIER_TIMELOCK` has elapsed. + event DkgFoldAttestationVerifierProposed( + address indexed verifier, + uint256 readyAt + ); + + /// @notice Emitted when the proposed DKG fold-attestation verifier becomes active. + event DkgFoldAttestationVerifierUpdated(address indexed verifier); + + /// @notice Emitted when the owner cancels a pending verifier proposal. + event DkgFoldAttestationVerifierProposalCancelled(address indexed verifier); + /// @notice This event MUST be emitted when a ciphernode is added to the registry. /// @param node Address of the ciphernode. /// @param index Index of the ciphernode in the registry. @@ -184,6 +198,24 @@ interface ICiphernodeRegistry { /// @param sortitionSubmissionWindow The submission window for the E3 sortition in seconds. event SortitionSubmissionWindowSet(uint256 sortitionSubmissionWindow); + /// @notice Emitted whenever the registry-wide accusation vote validity window changes. + /// @param accusationVoteValidity New validity window, in seconds. + event AccusationVoteValiditySet(uint256 accusationVoteValidity); + + /// @notice Emitted when the owner proposes a new accusation vote validity value. + /// @param accusationVoteValidity Pending validity window, in seconds. + /// @param readyAt Earliest timestamp when commit is allowed. + event AccusationVoteValidityProposed( + uint256 accusationVoteValidity, + uint256 readyAt + ); + + /// @notice Emitted when the owner cancels a pending accusation vote validity proposal. + /// @param accusationVoteValidity Pending value that was cancelled. + event AccusationVoteValidityProposalCancelled( + uint256 accusationVoteValidity + ); + //////////////////////////////////////////////////////////// // // // Errors // @@ -226,6 +258,52 @@ interface ICiphernodeRegistry { /// @notice Supplied DKG aggregator proof failed verification error InvalidDkgProof(); + /// @notice Proof aggregation enabled but no fold attestation bundle was supplied + error FoldAttestationsRequired(); + + /// @notice `dkgFoldAttestationVerifier` is not configured on the registry + error FoldAttestationVerifierNotSet(); + + /// @notice Initial verifier set was attempted after one was already configured. + /// @dev Subsequent changes must go through `proposeDkgFoldAttestationVerifier` → + /// `commitDkgFoldAttestationVerifier` (timelocked). + error FoldAttestationVerifierAlreadySet(); + + /// @notice Fold attestation bundle failed signature or public-input binding checks + error InvalidFoldAttestation(); + + /// @notice `partyId` from an attestation does not appear in the DKG proof public inputs + error PartyIdNotInProof(); + + /// @notice Attestation count does not match bindings or proof honest-set size + error AttestationBindingCountMismatch(); + + /// @notice `partyId` is out of bounds for the finalized committee's `topNodes` + error PartyIdOutOfBounds(uint256 partyId, uint256 committeeSize); + + /// @notice A `setDkgFoldAttestationVerifier` commit was attempted before the timelock elapsed. + error VerifierUpdateTimelockActive(uint256 readyAt, uint256 nowAt); + + /// @notice `commitDkgFoldAttestationVerifier` was called but no proposal is pending. + error NoPendingVerifierUpdate(); + + /// @notice `commitDkgFoldAttestationVerifier` was called with an address that does + /// not match the pending proposal (prevents commit-time substitution). + error VerifierMismatch(address pending, address provided); + + /// @notice A vote-validity commit was attempted before timelock elapsed. + error AccusationVoteValidityTimelockActive(uint256 readyAt, uint256 nowAt); + + /// @notice `commitAccusationVoteValidity` was called but no proposal is pending. + error NoPendingAccusationVoteValidityUpdate(); + + /// @notice `commitAccusationVoteValidity` was called with value that does not match pending. + error AccusationVoteValidityMismatch(uint256 pending, uint256 provided); + + /// @notice Directly setting `accusationVoteValidity` to zero is disallowed. + /// Use `proposeAccusationVoteValidity` + `commitAccusationVoteValidity`. + error AccusationVoteValidityZeroRequiresTimelock(); + /// @notice Node has already submitted a ticket for this E3 error NodeAlreadySubmitted(); @@ -325,13 +403,33 @@ interface ICiphernodeRegistry { /// @param pkCommitment Hash-based aggregated PK commitment for the committee. /// @param proof DkgAggregator (EVM) proof ABI-encoded `(bytes rawProof, bytes32[] publicInputs)`, /// or empty bytes when proof aggregation is disabled. + /// @param dkgAttestationBundle ABI-encoded + /// `(DkgFoldAttestationLib.Attestation[] attestations, DkgFoldAttestationLib.PartySlotBinding[] bindings)`. + /// Required (non-empty) when proof aggregation is enabled; ignored otherwise. function publishCommittee( uint256 e3Id, bytes calldata publicKey, bytes32 pkCommitment, - bytes calldata proof + bytes calldata proof, + bytes calldata dkgAttestationBundle ) external; + /// @notice Returns DKG anchor commitments stored at publication (empty if not yet published). + /// @param e3Id ID of the E3 + /// @return partyIds Honest party ids (same order as stored sk/esm arrays) + /// @return skAggCommits Per-party secret-key aggregate commitments from NodeFold + /// @return esmAggCommits Per-party smudging-noise aggregate commitments from NodeFold + function getDkgAnchors( + uint256 e3Id + ) + external + view + returns ( + uint256[] memory partyIds, + bytes32[] memory skAggCommits, + bytes32[] memory esmAggCommits + ); + /// @notice This function should be called by the Enclave contract to get the public key of a committee. /// @dev This function MUST revert if no committee has been requested for the given E3. /// @dev This function MUST revert if the committee has not yet published a public key. @@ -393,6 +491,28 @@ interface ICiphernodeRegistry { uint256 _sortitionSubmissionWindow ) external; + /// @notice Returns registry-wide accusation vote validity window (seconds). + function accusationVoteValidity() external view returns (uint256); + + /// @notice Sets nonzero accusation vote validity directly. + /// @dev Setting zero requires timelocked propose/commit flow. + function setAccusationVoteValidity( + uint256 _accusationVoteValidity + ) external; + + /// @notice Propose accusation vote validity (supports zero). + function proposeAccusationVoteValidity( + uint256 _accusationVoteValidity + ) external; + + /// @notice Commit accusation vote validity after timelock. + function commitAccusationVoteValidity( + uint256 _accusationVoteValidity + ) external; + + /// @notice Cancel a pending accusation vote validity proposal. + function cancelAccusationVoteValidityProposal() external; + /// @notice Submit a ticket for sortition /// @dev Validates ticket against node's balance at request block /// @param e3Id ID of the E3 computation @@ -448,6 +568,22 @@ interface ICiphernodeRegistry { address node ) external view returns (bool); + /// @notice Return the operator address at slot `partyId` in the finalized + /// committee's `topNodes` (address-ascending order). + /// @dev Unlike `getCommitteeNodes`, this does NOT require the committee to + /// be published yet — it works as soon as the committee is finalized, + /// which is what `publishCommittee` callers need in order to bind a + /// `partyId` to a specific operator before the public key is stored. + /// Reverts `CommitteeNotFinalized` for non-finalized committees so the + /// provisional, sortition-in-progress `topNodes` is never exposed. + /// @param e3Id ID of the E3 computation + /// @param partyId Index into `topNodes` + /// @return Operator address at `topNodes[partyId]` + function canonicalCommitteeNodeAt( + uint256 e3Id, + uint256 partyId + ) external view returns (address); + /// @notice Get active (non-expelled) committee nodes for an E3 /// @param e3Id ID of the E3 computation /// @return nodes Array of active committee member addresses diff --git a/packages/enclave-contracts/contracts/interfaces/IDkgFoldAttestationVerifier.sol b/packages/enclave-contracts/contracts/interfaces/IDkgFoldAttestationVerifier.sol new file mode 100644 index 0000000000..5618801553 --- /dev/null +++ b/packages/enclave-contracts/contracts/interfaces/IDkgFoldAttestationVerifier.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +pragma solidity 0.8.28; + +/** + * @title IDkgFoldAttestationVerifier + * @notice Verifies per-node DKG fold ECDSA attestations against a DkgAggregator proof. + * @dev Invoked via external call from the registry so verification does not share its stack frame. + */ +interface IDkgFoldAttestationVerifier { + /// @notice Verify attestations and return anchor arrays indexed by proof party-id slot. + /// @param registry Ciphernode registry (`isCommitteeMemberActive` callback). + /// @param chainId EIP-712 chain id used in attestation digests. + /// @param e3Id E3 identifier. + /// @param proof ABI-encoded `(bytes rawProof, bytes32[] publicInputs)` from the aggregator. + /// @param dkgAttestationBundle ABI-encoded + /// `(Attestation[] attestations, PartySlotBinding[] bindings)`; bindings sorted by ascending `partyId`. + /// @return partyIds Honest party ids (proof slot order) + /// @return skAggCommits Per-party sk aggregate commitments + /// @return esmAggCommits Per-party esm aggregate commitments + function verify( + address registry, + uint256 chainId, + uint256 e3Id, + bytes calldata proof, + bytes calldata dkgAttestationBundle + ) + external + view + returns ( + uint256[] memory partyIds, + bytes32[] memory skAggCommits, + bytes32[] memory esmAggCommits + ); +} diff --git a/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol b/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol index d0d4def9e2..866ca5dfb2 100644 --- a/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol +++ b/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol @@ -7,6 +7,7 @@ pragma solidity 0.8.28; import { IBondingRegistry } from "./IBondingRegistry.sol"; +import { ICiphernodeRegistry } from "./ICiphernodeRegistry.sol"; import { IE3RefundManager } from "./IE3RefundManager.sol"; /** @@ -42,7 +43,8 @@ interface ISlashingManager { * @param ticketPenalty Amount of ticket collateral to slash (in wei) * @param licensePenalty Amount of license bond to slash (in wei) * @param requiresProof Whether this slash type requires cryptographic proof verification - * @param proofVerifier Address of the ISlashVerifier contract for proof validation + * @param proofVerifier Optional ISlashVerifier for ZK-based slashes; Lane A (`proposeSlash`) + * uses on-chain attestation verification and may leave this as `address(0)`. * @param banNode Whether executing this slash will permanently ban the node * @param appealWindow Time window in seconds for operators to appeal (0 = immediate execution, no appeals) * @param enabled Whether this slash type is currently active and can be proposed @@ -133,6 +135,9 @@ interface ISlashingManager { /// @notice Thrown when the operator is not a member of the committee for this E3 error OperatorNotInCommittee(); + /// @notice Thrown when a requested DKG `partyId` is not present in stored anchors for this E3 + error PartyIdNotInDkgAnchors(); + /// @notice Thrown when the verifier address in signed evidence doesn't match the policy's current verifier error VerifierMismatch(); @@ -418,6 +423,15 @@ interface ISlashingManager { view returns (IBondingRegistry registry); + /** + * @notice Returns the ciphernode registry contract used for committee checks and DKG anchors + * @return registry Address of the ciphernode registry + */ + function ciphernodeRegistry() + external + view + returns (ICiphernodeRegistry registry); + // ====================== // Admin Functions // ====================== @@ -431,8 +445,8 @@ interface ISlashingManager { * - reason must not be bytes32(0) * - policy.enabled must be true * - At least one of ticketPenalty or licensePenalty must be non-zero - * - If requiresProof is true, proofVerifier must be set and appealWindow must be 0 - * - If requiresProof is false, appealWindow must be greater than 0 + * - If requiresProof is true (Lane A), appealWindow may be 0 (immediate execute) or > 0 + * - If requiresProof is false (Lane B), appealWindow must be greater than 0 */ function setSlashPolicy( bytes32 reason, @@ -481,19 +495,21 @@ interface ISlashingManager { * cross-reason replay attacks. * Evidence format: * abi.encode(uint256 proofType, - * address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures) + * address[] voters, bytes32[] dataHashes, bytes evidence, uint256 deadline, bytes[] signatures) * Each voter must have signed: personal_sign(keccak256(abi.encode(VOTE_TYPEHASH, - * block.chainid, e3Id, accusationId, voter, agrees, dataHash))) + * e3Id, accusationId, voter, dataHash, deadline))) * where accusationId = keccak256(abi.encodePacked(block.chainid, e3Id, operator, proofType)) * Verifications performed: * 1. Number of votes >= committee threshold M * 2. Voters are sorted ascending (prevents duplicates) * 3. Each voter is a committee member for this E3 * 4. Each vote signature recovers to the declared voter - * 5. All votes agree the proof is invalid (agrees == true) + * 5. All votes carry the same `dataHash` (no equivocation) + * 6. `keccak256(evidence) == dataHash` * @param e3Id ID of the E3 computation this slash relates to * @param operator Address of the ciphernode operator to slash (must be non-zero) - * @param proof Attestation evidence: abi.encode(proofType, voters, agrees, dataHashes, signatures) + * @param proof Attestation evidence: + * abi.encode(proofType, voters, dataHashes, evidence, deadline, signatures) * @return proposalId Sequential ID of the created proposal */ function proposeSlash( @@ -502,6 +518,22 @@ interface ISlashingManager { bytes calldata proof ) external returns (uint256 proposalId); + /** + * @notice Creates a new Lane A slash proposal by DKG `partyId` attribution. + * @dev Resolves `operator = topNodes[partyId]` and requires `partyId` to be present in + * `CiphernodeRegistry.getDkgAnchors(e3Id).partyIds` before processing attestation evidence. + * This provides an explicit on-chain chain from DKG fold row/slot attribution to operator. + * @param e3Id ID of the E3 computation this slash relates to + * @param partyId Canonical committee slot / DKG party identifier + * @param proof Attestation evidence: abi.encode(proofType, voters, dataHashes, deadline, signatures) + * @return proposalId Sequential ID of the created proposal + */ + function proposeSlashByDkgParty( + uint256 e3Id, + uint256 partyId, + bytes calldata proof + ) external returns (uint256 proposalId); + /** * @notice Creates a new slash proposal with evidence (Lane B - SLASHER_ROLE required) * @dev Only callable by SLASHER_ROLE. Evidence-based slashes have appeal windows. diff --git a/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol b/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol index 9d72a15f53..c241fc03fc 100644 --- a/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol +++ b/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol @@ -14,10 +14,26 @@ pragma solidity 0.8.28; library CommitteeHashLib { uint256 private constant _LO_MASK = (uint256(1) << 128) - 1; - /// @notice `keccak256(abi.encodePacked(nodes))` for the ordered on-chain committee. - /// @dev Callers pass `storage` arrays via implicit copy to this `memory` parameter. + /// @notice `keccak256(concat(20-byte addresses))` for the ordered on-chain committee. + /// @dev Must match `e3_utils::committee_hash::hash_committee_addresses`, which packs + /// each address as raw 20 bytes with no padding. NOTE: `abi.encodePacked(address[])` + /// pads each element to 32 bytes (left-padded), which does NOT match the off-chain + /// canonical encoding — so we build the 20*N byte buffer manually. function hash(address[] memory nodes) internal pure returns (bytes32) { - return keccak256(abi.encodePacked(nodes)); + uint256 n = nodes.length; + bytes memory packed = new bytes(n * 20); + for (uint256 i = 0; i < n; ++i) { + bytes20 a = bytes20(nodes[i]); + uint256 offset = i * 20; + // Write 20-byte address in one word store; trailing 12 bytes are + // zeroed and either overwritten by the next address or ignored by + // `keccak256(packed)` because bytes length is exactly `20*n`. + // solhint-disable-next-line no-inline-assembly + assembly { + mstore(add(add(packed, 0x20), offset), shl(96, a)) + } + } + return keccak256(packed); } /// @notice High 128 bits of a committee hash (Noir public input `committee_hash_hi`). diff --git a/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol b/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol new file mode 100644 index 0000000000..9a703a3813 --- /dev/null +++ b/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +pragma solidity 0.8.28; + +import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; + +/** + * @title DkgFoldAttestationLib + * @notice Canonical EIP-712 digests for per-node DKG fold attestations. + * @dev Must stay aligned with `DkgFoldAttestationPayload` in + * `crates/events/src/enclave_event/dkg_fold_attestation.rs`. + * + * Domain binds `chainId` and `verifyingContract` (the + * `DkgFoldAttestationVerifier` address); the struct binds `e3Id`, + * `partyId`, and the two NodeFold commitments. Signatures cannot be + * replayed across chains, verifier deployments, or E3s. + */ +library DkgFoldAttestationLib { + /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")` + bytes32 public constant EIP712_DOMAIN_TYPEHASH = + keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + + /// @dev `keccak256("EnclaveDkgFoldAttestation")` + bytes32 public constant DOMAIN_NAME_HASH = + keccak256(bytes("EnclaveDkgFoldAttestation")); + + /// @dev `keccak256("1")` + bytes32 public constant DOMAIN_VERSION_HASH = keccak256(bytes("1")); + + /// @dev `keccak256("DkgFoldAttestation(uint256 e3Id,uint256 partyId, + /// bytes32 skAggCommit,bytes32 esmAggCommit)")` + bytes32 public constant TYPEHASH = + keccak256( + "DkgFoldAttestation(uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)" + ); + + /// @notice One node's signed fold output. + struct Attestation { + uint256 partyId; + bytes32 skAggCommit; + bytes32 esmAggCommit; + bytes signature; + } + + /// @notice Maps sortition `partyId` to the operator that produced the fold. + struct PartySlotBinding { + uint256 partyId; + address node; + } + + /// @notice EIP-712 domain separator bound to `chainId` and `verifyingContract`. + function domainSeparator( + uint256 chainId, + address verifyingContract + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + EIP712_DOMAIN_TYPEHASH, + DOMAIN_NAME_HASH, + DOMAIN_VERSION_HASH, + chainId, + verifyingContract + ) + ); + } + + /// @notice `hashStruct(DkgFoldAttestation)` per EIP-712. + function structHash( + uint256 e3Id, + uint256 partyId, + bytes32 skAggCommit, + bytes32 esmAggCommit + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode(TYPEHASH, e3Id, partyId, skAggCommit, esmAggCommit) + ); + } + + /// @notice EIP-712 typed-data hash: `keccak256("\x19\x01" || domainSeparator || structHash)`. + function typedDataHash( + uint256 chainId, + address verifyingContract, + uint256 e3Id, + uint256 partyId, + bytes32 skAggCommit, + bytes32 esmAggCommit + ) internal pure returns (bytes32) { + return + keccak256( + abi.encodePacked( + "\x19\x01", + domainSeparator(chainId, verifyingContract), + structHash(e3Id, partyId, skAggCommit, esmAggCommit) + ) + ); + } + + /// @notice Recover the EIP-712 signer for a fold attestation. + function recoverSigner( + uint256 chainId, + address verifyingContract, + uint256 e3Id, + Attestation memory attestation + ) internal pure returns (address) { + bytes32 digest = typedDataHash( + chainId, + verifyingContract, + e3Id, + attestation.partyId, + attestation.skAggCommit, + attestation.esmAggCommit + ); + return ECDSA.recover(digest, attestation.signature); + } +} diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index c090499924..73dc2cff04 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -21,12 +21,16 @@ import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { CommitteeHashLib } from "../lib/CommitteeHashLib.sol"; +import { + IDkgFoldAttestationVerifier +} from "../interfaces/IDkgFoldAttestationVerifier.sol"; /** * @title CiphernodeRegistryOwnable * @notice Ownable implementation of the ciphernode registry with IMT-based membership tracking * @dev Manages ciphernode registration, committee selection, and integrates with bonding registry */ +// solhint-disable-next-line max-states-count contract CiphernodeRegistryOwnable is ICiphernodeRegistry, Ownable2StepUpgradeable @@ -117,6 +121,53 @@ contract CiphernodeRegistryOwnable is /// @notice Address of the slashing manager authorized to expel committee members ISlashingManager public slashingManager; + /// @notice Verifies per-node DKG fold attestations at publication (external contract). + IDkgFoldAttestationVerifier public dkgFoldAttestationVerifier; + + /// @notice Minimum delay between proposing a verifier change and committing it. + /// @dev Treats `dkgFoldAttestationVerifier` as a critical admin key: a compromised + /// owner cannot instantly swap it for a weak verifier that bypasses + /// per-party attestation checks; the proposal is visible on-chain for + /// this window and can be cancelled by the (recovered) legitimate owner. + uint256 public constant DKG_FOLD_VERIFIER_TIMELOCK = 2 days; + + /// @notice Pending verifier proposal awaiting commit. `pendingAt == 0` means no proposal. + address public pendingDkgFoldAttestationVerifier; + uint256 public pendingDkgFoldAttestationVerifierAt; + + /// @notice Registry-wide validity window (seconds) accusers stamp on accusation + /// vote signatures. Ciphernodes fetch this on startup and add it to the + /// current wall-clock when populating `AccusationVote.deadline`. The + /// on-chain `SlashingManager._verifyAttestationEvidence` then enforces + /// `block.timestamp <= deadline`, so this value bounds how long a leaked + /// vote signature stays replayable. + /// + /// @dev Set with [`setAccusationVoteValidity`] by `owner()`. Defaults to the + /// [`DEFAULT_ACCUSATION_VOTE_VALIDITY`] constant on initialize so newly- + /// deployed registries are operational without an extra setter call. + /// Setting to zero disables the off-chain freshness window (deadlines + /// collapse to "now", effectively rejecting every vote on chain) — intentionally + /// allowed so governance can hard-stop slashing in an emergency. + uint256 public accusationVoteValidity; + + /// @notice Default value for `accusationVoteValidity` applied at `initialize`. + /// @dev 30 minutes covers gossip latency, vote-collection timeout, and mempool + /// congestion while keeping stolen signatures from being replayed indefinitely. + uint256 public constant DEFAULT_ACCUSATION_VOTE_VALIDITY = 30 minutes; + + /// @notice Minimum delay between proposing and committing a zeroing vote-validity update. + /// @dev Mirrors verifier critical-change timelock posture for slash-disable behavior. + uint256 public constant ACCUSATION_VOTE_VALIDITY_TIMELOCK = 2 days; + + /// @notice Pending vote-validity proposal awaiting commit. `pendingAt == 0` means none. + uint256 public pendingAccusationVoteValidity; + uint256 public pendingAccusationVoteValidityAt; + + /// @notice DKG anchor commitments stored when the committee public key is published. + mapping(uint256 e3Id => uint256[] partyIds) internal dkgPartyIds; + mapping(uint256 e3Id => bytes32[] skAggCommits) internal dkgSkAggCommits; + mapping(uint256 e3Id => bytes32[] esmAggCommits) internal dkgEsmAggCommits; + //////////////////////////////////////////////////////////// // // // Modifiers // @@ -170,9 +221,17 @@ contract CiphernodeRegistryOwnable is ) public initializer { require(_owner != address(0), ZeroAddress()); + // Hold ownership transiently as `msg.sender` so the internal call to + // `setSortitionSubmissionWindow` (which is `onlyOwner`) succeeds, then + // transfer to the final `_owner` before returning. __Ownable_init(msg.sender); ciphernodes._init(TREE_DEPTH); setSortitionSubmissionWindow(_submissionWindow); + // Seed the off-chain freshness window with a sensible default so new + // deployments don't immediately need a governance call before slashing + // becomes operational. + accusationVoteValidity = DEFAULT_ACCUSATION_VOTE_VALIDITY; + emit AccusationVoteValiditySet(DEFAULT_ACCUSATION_VOTE_VALIDITY); if (_owner != owner()) _transferOwnership(_owner); } @@ -231,7 +290,8 @@ contract CiphernodeRegistryOwnable is uint256 e3Id, bytes calldata publicKey, bytes32 pkCommitment, - bytes calldata proof + bytes calldata proof, + bytes calldata dkgAttestationBundle ) external { Committee storage c = committees[e3Id]; @@ -249,19 +309,18 @@ contract CiphernodeRegistryOwnable is E3 memory e3 = enclave.getE3(e3Id); if (e3.proofAggregationEnabled) { - require(proof.length > 0, DkgProofRequired()); - // Wrapper binds proof to full call context (chainId, this, e3Id, committeeRoot, - // sortedNodes, pkCommitment, committeeHash) and anchors recursive-aggregation VKs - // against immutables; reverts on mismatch with a typed error. Bind to the on-chain - // selected committee (`c.topNodes`), not caller-supplied `nodes`, so a wrong - // `nodes` input cannot pre-commit the prover to the attacker's set. - e3.pkVerifier.verify( + // Bind to the on-chain committee (c.topNodes), not caller-supplied + // nodes, so a wrong `nodes` input cannot pre-commit the prover to + // an attacker's set (C-08). + _verifyAndStoreDkgAnchors( e3Id, + e3, roots[e3Id], c.topNodes, pkCommitment, committeeHash, - proof + proof, + dkgAttestationBundle ); } @@ -276,6 +335,149 @@ contract CiphernodeRegistryOwnable is ); } + function _verifyAndStoreDkgAnchors( + uint256 e3Id, + E3 memory e3, + uint256 committeeRoot, + address[] memory sortedNodes, + bytes32 pkCommitment, + bytes32 committeeHash, + bytes calldata proof, + bytes calldata dkgAttestationBundle + ) internal { + require(proof.length > 0, DkgProofRequired()); + // Reverts with a typed error on any mismatch; binds to the on-chain + // committee (sortedNodes = c.topNodes) per audit finding C-08. + e3.pkVerifier.verify( + e3Id, + committeeRoot, + sortedNodes, + pkCommitment, + committeeHash, + proof + ); + _verifyAndStoreFoldAttestation(e3Id, proof, dkgAttestationBundle); + } + + /// @dev Split out to avoid "stack too deep" in `_verifyAndStoreDkgAnchors`. + function _verifyAndStoreFoldAttestation( + uint256 e3Id, + bytes calldata proof, + bytes calldata dkgAttestationBundle + ) internal { + require(dkgAttestationBundle.length > 0, FoldAttestationsRequired()); + require( + address(dkgFoldAttestationVerifier) != address(0), + FoldAttestationVerifierNotSet() + ); + + ( + uint256[] memory partyIds, + bytes32[] memory skAgg, + bytes32[] memory esmAgg + ) = dkgFoldAttestationVerifier.verify( + address(this), + block.chainid, + e3Id, + proof, + dkgAttestationBundle + ); + + dkgPartyIds[e3Id] = partyIds; + dkgSkAggCommits[e3Id] = skAgg; + dkgEsmAggCommits[e3Id] = esmAgg; + } + + /// @notice Propose a new DKG fold-attestation verifier. The change becomes active + /// only after `DKG_FOLD_VERIFIER_TIMELOCK` has elapsed and `commitDkgFoldAttestationVerifier` + /// is called. Replaces any pending proposal. + /// @dev First-time set is also subject to the timelock — operators must wait + /// the same window before the verifier is active. For the deploy-time + /// initial set, see `setInitialDkgFoldAttestationVerifier`. + /// + /// @dev **Node-operator requirement.** Ciphernodes fetch + /// `dkgFoldAttestationVerifier()` from this registry **once at process + /// startup** and use the returned address as the EIP-712 `verifyingContract` + /// for every fold attestation they sign during the process lifetime. + /// After a successful `commitDkgFoldAttestationVerifier`, signatures + /// produced by long-running nodes will be rejected on-chain by the new + /// verifier (different `verifyingContract` → different EIP-712 domain + /// separator → `ECDSA.recover` returns the wrong address). + /// + /// Operators MUST restart all ciphernodes within `DKG_FOLD_VERIFIER_TIMELOCK` + /// after this function is called — the 2-day window is sized to give + /// operators time to coordinate a rolling restart before the swap + /// becomes effective. Nodes that miss the window will silently produce + /// invalid fold attestations and be treated as dishonest by aggregators + /// until they restart. + function proposeDkgFoldAttestationVerifier( + IDkgFoldAttestationVerifier verifier + ) external onlyOwner { + require(address(verifier) != address(0), ZeroAddress()); + pendingDkgFoldAttestationVerifier = address(verifier); + pendingDkgFoldAttestationVerifierAt = block.timestamp; + emit DkgFoldAttestationVerifierProposed( + address(verifier), + block.timestamp + DKG_FOLD_VERIFIER_TIMELOCK + ); + } + + /// @notice Commit a previously proposed verifier change after the timelock elapses. + /// @param verifier Must match the pending proposal (prevents commit-time substitution). + function commitDkgFoldAttestationVerifier( + IDkgFoldAttestationVerifier verifier + ) external onlyOwner { + address pending = pendingDkgFoldAttestationVerifier; + require(pending != address(0), NoPendingVerifierUpdate()); + require( + pending == address(verifier), + VerifierMismatch(pending, address(verifier)) + ); + uint256 readyAt = pendingDkgFoldAttestationVerifierAt + + DKG_FOLD_VERIFIER_TIMELOCK; + require( + block.timestamp >= readyAt, + VerifierUpdateTimelockActive(readyAt, block.timestamp) + ); + dkgFoldAttestationVerifier = verifier; + pendingDkgFoldAttestationVerifier = address(0); + pendingDkgFoldAttestationVerifierAt = 0; + emit DkgFoldAttestationVerifierUpdated(address(verifier)); + } + + /// @notice Cancel a pending verifier proposal. + function cancelDkgFoldAttestationVerifierProposal() external onlyOwner { + address pending = pendingDkgFoldAttestationVerifier; + require(pending != address(0), NoPendingVerifierUpdate()); + pendingDkgFoldAttestationVerifier = address(0); + pendingDkgFoldAttestationVerifierAt = 0; + emit DkgFoldAttestationVerifierProposalCancelled(pending); + } + + /// @notice One-shot initial set, allowed only when no verifier has ever been configured. + /// @dev Lets deploy scripts wire the verifier without first waiting the timelock. + /// Subsequent changes must go through `propose`/`commit`. Cannot be used to + /// bypass the timelock for replacement — only for the very first set. + function setInitialDkgFoldAttestationVerifier( + IDkgFoldAttestationVerifier verifier + ) external onlyOwner { + require( + address(dkgFoldAttestationVerifier) == address(0), + FoldAttestationVerifierAlreadySet() + ); + require(address(verifier) != address(0), ZeroAddress()); + dkgFoldAttestationVerifier = verifier; + // Invalidate any stale pending proposal made before the initial set, + // so it cannot later be committed and silently bypass the timelock. + if (pendingDkgFoldAttestationVerifier != address(0)) { + address staleProposal = pendingDkgFoldAttestationVerifier; + pendingDkgFoldAttestationVerifier = address(0); + pendingDkgFoldAttestationVerifierAt = 0; + emit DkgFoldAttestationVerifierProposalCancelled(staleProposal); + } + emit DkgFoldAttestationVerifierUpdated(address(verifier)); + } + /// @inheritdoc ICiphernodeRegistry function addCiphernode(address node) external onlyOwnerOrBondingVault { if (isEnabled(node)) { @@ -401,6 +603,8 @@ contract CiphernodeRegistryOwnable is return false; } + _sortTopNodesByAscendingAddress(c); + c.stage = ICiphernodeRegistry.CommitteeStage.Finalized; c.activeCount = c.topNodes.length; @@ -470,6 +674,73 @@ contract CiphernodeRegistryOwnable is emit SortitionSubmissionWindowSet(_sortitionSubmissionWindow); } + /// @notice Update the registry-wide vote validity window used by accusers + /// when stamping `AccusationVote.deadline`. + /// @dev Ciphernodes fetch this once at startup. After a change, in-flight + /// ciphernode processes continue to use the previous value until + /// restarted — operators should coordinate a restart if the new + /// window is materially shorter than the old one, otherwise stale + /// nodes will produce votes the on-chain verifier rejects. + /// @param _accusationVoteValidity New validity window in seconds. + /// Zero is allowed and intentionally disables slashing submission + /// until governance restores a nonzero value. + function setAccusationVoteValidity( + uint256 _accusationVoteValidity + ) external onlyOwner { + require( + _accusationVoteValidity != 0, + AccusationVoteValidityZeroRequiresTimelock() + ); + accusationVoteValidity = _accusationVoteValidity; + emit AccusationVoteValiditySet(_accusationVoteValidity); + } + + /// @notice Propose a new accusation vote validity window (supports zero). + /// @dev Zeroing the window is slash-disable behavior and therefore timelocked. + function proposeAccusationVoteValidity( + uint256 _accusationVoteValidity + ) external onlyOwner { + pendingAccusationVoteValidity = _accusationVoteValidity; + pendingAccusationVoteValidityAt = block.timestamp; + emit AccusationVoteValidityProposed( + _accusationVoteValidity, + block.timestamp + ACCUSATION_VOTE_VALIDITY_TIMELOCK + ); + } + + /// @notice Commit a previously proposed accusation vote validity update. + /// @param _accusationVoteValidity Must match the pending proposal. + function commitAccusationVoteValidity( + uint256 _accusationVoteValidity + ) external onlyOwner { + uint256 pendingAt = pendingAccusationVoteValidityAt; + require(pendingAt != 0, NoPendingAccusationVoteValidityUpdate()); + uint256 pending = pendingAccusationVoteValidity; + require( + pending == _accusationVoteValidity, + AccusationVoteValidityMismatch(pending, _accusationVoteValidity) + ); + uint256 readyAt = pendingAt + ACCUSATION_VOTE_VALIDITY_TIMELOCK; + require( + block.timestamp >= readyAt, + AccusationVoteValidityTimelockActive(readyAt, block.timestamp) + ); + accusationVoteValidity = _accusationVoteValidity; + pendingAccusationVoteValidity = 0; + pendingAccusationVoteValidityAt = 0; + emit AccusationVoteValiditySet(_accusationVoteValidity); + } + + /// @notice Cancel a pending accusation vote validity proposal. + function cancelAccusationVoteValidityProposal() external onlyOwner { + uint256 pendingAt = pendingAccusationVoteValidityAt; + require(pendingAt != 0, NoPendingAccusationVoteValidityUpdate()); + uint256 pending = pendingAccusationVoteValidity; + pendingAccusationVoteValidity = 0; + pendingAccusationVoteValidityAt = 0; + emit AccusationVoteValidityProposalCancelled(pending); + } + //////////////////////////////////////////////////////////// // // // Get Functions // @@ -541,6 +812,26 @@ contract CiphernodeRegistryOwnable is committeeHash = c.committeeHash; } + /// @inheritdoc ICiphernodeRegistry + function getDkgAnchors( + uint256 e3Id + ) + external + view + returns ( + uint256[] memory partyIds, + bytes32[] memory skAggCommits, + bytes32[] memory esmAggCommits + ) + { + require(publicKeyHashes[e3Id] != bytes32(0), CommitteeNotPublished()); + return ( + dkgPartyIds[e3Id], + dkgSkAggCommits[e3Id], + dkgEsmAggCommits[e3Id] + ); + } + /// @notice Returns the current size of the ciphernode IMT /// @return Size of the IMT function treeSize() public view returns (uint256) { @@ -625,6 +916,26 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry.MemberStatus.None; } + /// @inheritdoc ICiphernodeRegistry + function canonicalCommitteeNodeAt( + uint256 e3Id, + uint256 partyId + ) external view returns (address) { + Committee storage c = committees[e3Id]; + // Only expose `partyId -> node` for canonical (finalized) committees. + // Pre-finalization, `topNodes` is still being populated by sortition + // and is not the canonical mapping. + require( + c.stage == ICiphernodeRegistry.CommitteeStage.Finalized, + CommitteeNotFinalized() + ); + require( + partyId < c.topNodes.length, + PartyIdOutOfBounds(partyId, c.topNodes.length) + ); + return c.topNodes[partyId]; + } + /// @inheritdoc ICiphernodeRegistry function getActiveCommitteeNodes( uint256 e3Id @@ -734,6 +1045,27 @@ contract CiphernodeRegistryOwnable is require(ticketNumber <= availableTickets, InvalidTicketNumber()); } + /// @notice Sort `topNodes` by ascending address before committee finalization. + /// @dev Canonical address-ascending order so `CommitteeHashLib.hash(topNodes)` + /// matches what off-chain aggregators independently compute over the same + /// address set (Rust uses `BTreeSet` which iterates lexicographically, + /// equivalent to numeric address-ascending for hex-encoded addresses). + /// This also defines `party_id` = position in the address-sorted committee. + /// @param c Committee storage reference + function _sortTopNodesByAscendingAddress(Committee storage c) internal { + uint256 len = c.topNodes.length; + for (uint256 i = 0; i < len; ++i) { + for (uint256 j = i + 1; j < len; ++j) { + address left = c.topNodes[i]; + address right = c.topNodes[j]; + if (right < left) { + c.topNodes[i] = right; + c.topNodes[j] = left; + } + } + } + } + /// @notice Inserts a node into the top-N list - Smallest scores /// @dev O(N) linear scan per insertion to find the worst score. For a committee of size N /// with S total submissions, total gas is O(N * S). With N=20 and S=1000, this is ~20K diff --git a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol index c8dfdc2344..0c4b3a0419 100644 --- a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol +++ b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol @@ -142,6 +142,30 @@ contract SlashingManager is "address voter,bytes32 dataHash,uint256 deadline)" ); + /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. + /// Exposed for off-chain signers that recompute the domain separator manually + /// (e.g. `AccusationManager::vote_domain_separator` in the Rust prover crate). + bytes32 public constant EIP712_DOMAIN_TYPEHASH = + keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + + /// @dev EIP-712 domain `name`. Must match the literal passed to `EIP712(...)` + /// in the constructor below; off-chain signers MUST hash this exact byte + /// string for `recover` to match. + string public constant EIP712_DOMAIN_NAME = "EnclaveSlashing"; + + /// @dev EIP-712 domain `version`. Same alignment rule as `EIP712_DOMAIN_NAME`. + string public constant EIP712_DOMAIN_VERSION = "1"; + + /// @dev `keccak256(bytes(EIP712_DOMAIN_NAME))`. + bytes32 public constant DOMAIN_NAME_HASH = + keccak256(bytes(EIP712_DOMAIN_NAME)); + + /// @dev `keccak256(bytes(EIP712_DOMAIN_VERSION))`. + bytes32 public constant DOMAIN_VERSION_HASH = + keccak256(bytes(EIP712_DOMAIN_VERSION)); + // ====================== // Modifiers // ====================== @@ -179,7 +203,7 @@ contract SlashingManager is address admin ) AccessControlDefaultAdminRules(initialDelay, admin) - EIP712("EnclaveSlashing", "1") + EIP712(EIP712_DOMAIN_NAME, EIP712_DOMAIN_VERSION) { require(admin != address(0), ZeroAddress()); _grantRole(GOVERNANCE_ROLE, admin); @@ -320,14 +344,34 @@ contract SlashingManager is /// Execution is atomic when `policy.appealWindow == 0`, otherwise deferred so /// the accused can {fileAppeal}. Evidence format: /// `abi.encode(uint256 proofType, address[] voters, bytes32[] dataHashes, - /// uint256 deadline, bytes[] signatures)`. Voters sign the EIP-712 + /// bytes evidence, uint256 deadline, bytes[] signatures)`. Voters sign the EIP-712 /// `AccusationVote` against this contract's domain; all `dataHash` values - /// must be identical or the call reverts with `EquivocationDetected`. + /// must be identical and equal `keccak256(evidence)`. function proposeSlash( uint256 e3Id, address operator, bytes calldata proof ) external returns (uint256 proposalId) { + return _proposeSlash(e3Id, operator, proof); + } + + /// @inheritdoc ISlashingManager + /// @dev Additive attribution path: resolves `operator` from DKG anchors and + /// canonical committee slot before reusing Lane A attestation validation. + function proposeSlashByDkgParty( + uint256 e3Id, + uint256 partyId, + bytes calldata proof + ) external returns (uint256 proposalId) { + address operator = _resolveDkgPartyOperator(e3Id, partyId); + return _proposeSlash(e3Id, operator, proof); + } + + function _proposeSlash( + uint256 e3Id, + address operator, + bytes calldata proof + ) internal returns (uint256 proposalId) { require(operator != address(0), ZeroAddress()); require(proof.length != 0, ProofRequired()); @@ -393,6 +437,27 @@ contract SlashingManager is } } + /// @dev Resolve a slash target from DKG anchors: + /// - `partyId` must be present in `getDkgAnchors(e3Id).partyIds` + /// - operator is resolved as canonical committee slot `topNodes[partyId]` + function _resolveDkgPartyOperator( + uint256 e3Id, + uint256 partyId + ) internal view returns (address operator) { + (uint256[] memory partyIds, , ) = ciphernodeRegistry.getDkgAnchors( + e3Id + ); + bool found = false; + for (uint256 i = 0; i < partyIds.length; i++) { + if (partyIds[i] == partyId) { + found = true; + break; + } + } + require(found, PartyIdNotInDkgAnchors()); + return ciphernodeRegistry.canonicalCommitteeNodeAt(e3Id, partyId); + } + /// @inheritdoc ISlashingManager /// @dev Lane B: Evidence-based slash with appeal window. SLASHER_ROLE required. function proposeSlashEvidence( @@ -483,7 +548,8 @@ contract SlashingManager is /// @dev Verifies Lane A attestation evidence: decodes, checks quorum (>= M), verifies /// each EIP-712 `AccusationVote` signature, confirms voters are active committee /// members, enforces the shared `deadline`, and rejects equivocation (all - /// `dataHash` values must match). Voters must be sorted ascending (no duplicates). + /// `dataHash` values must match and bind to `keccak256(evidence)`). + /// Voters must be sorted ascending (no duplicates). function _verifyAttestationEvidence( bytes calldata proof, uint256 e3Id, @@ -493,11 +559,12 @@ contract SlashingManager is uint256 proofType, address[] memory voters, bytes32[] memory dataHashes, + bytes memory evidence, uint256 deadline, bytes[] memory signatures ) = abi.decode( proof, - (uint256, address[], bytes32[], uint256, bytes[]) + (uint256, address[], bytes32[], bytes, uint256, bytes[]) ); uint256 numVotes = voters.length; @@ -525,6 +592,7 @@ contract SlashingManager is // one voter is signing inconsistent statements and the attestation must not be // accepted as a single fault witness. bytes32 sharedDataHash = dataHashes[0]; + require(keccak256(evidence) == sharedDataHash, InvalidProof()); // Verify each vote signature and membership address prevVoter = address(0); @@ -685,13 +753,12 @@ contract SlashingManager is // Only the accused can appeal require(msg.sender == p.operator, Unauthorized()); + // Already-executed slashes (Lane A with appealWindow == 0) cannot be appealed. + require(!p.executed, AlreadyExecuted()); // Only within the appeal window require(block.timestamp < p.executableAt, AppealWindowExpired()); // Only once require(!p.appealed, AlreadyAppealed()); - // A slash that has already been atomically executed (Lane A with - // `appealWindow == 0`) has no appeal window and cannot be appealed. - require(!p.executed, AlreadyExecuted()); p.appealed = true; diff --git a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol index d6c8bb6f8a..26085dacdb 100644 --- a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol @@ -15,6 +15,10 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { /// @notice Configurable threshold M per E3 for testing mapping(uint256 e3Id => uint32 threshold) private _thresholdM; + uint256 private _accusationVoteValidity = 30 minutes; + mapping(uint256 e3Id => uint256[] partyIds) private _dkgPartyIds; + mapping(uint256 e3Id => bytes32[] skAggCommits) private _dkgSkAggCommits; + mapping(uint256 e3Id => bytes32[] esmAggCommits) private _dkgEsmAggCommits; /// @notice Set committee members for an E3 (test helper) function setCommitteeNodes( @@ -32,6 +36,27 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { _thresholdM[e3Id] = m; } + /// @notice Set DKG anchors for an E3 (test helper) + function setDkgAnchors( + uint256 e3Id, + uint256[] calldata partyIds, + bytes32[] calldata skAggCommits, + bytes32[] calldata esmAggCommits + ) external { + delete _dkgPartyIds[e3Id]; + delete _dkgSkAggCommits[e3Id]; + delete _dkgEsmAggCommits[e3Id]; + for (uint256 i = 0; i < partyIds.length; i++) { + _dkgPartyIds[e3Id].push(partyIds[i]); + } + for (uint256 i = 0; i < skAggCommits.length; i++) { + _dkgSkAggCommits[e3Id].push(skAggCommits[i]); + } + for (uint256 i = 0; i < esmAggCommits.length; i++) { + _dkgEsmAggCommits[e3Id].push(esmAggCommits[i]); + } + } + function requestCommittee( uint256, uint256, @@ -70,6 +95,7 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { uint256, bytes calldata, bytes32, + bytes calldata, bytes calldata ) external pure {} // solhint-disable-line no-empty-blocks @@ -83,6 +109,24 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { return keccak256(abi.encodePacked(_committeeNodes[e3Id])); } + function getDkgAnchors( + uint256 e3Id + ) + external + view + returns ( + uint256[] memory partyIds, + bytes32[] memory skAggCommits, + bytes32[] memory esmAggCommits + ) + { + return ( + _dkgPartyIds[e3Id], + _dkgSkAggCommits[e3Id], + _dkgEsmAggCommits[e3Id] + ); + } + function root() external pure returns (uint256) { return 0; } @@ -117,6 +161,23 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { return 0; } + function accusationVoteValidity() external view returns (uint256) { + return _accusationVoteValidity; + } + + function setAccusationVoteValidity(uint256 v) external { + _accusationVoteValidity = v; + } + + // solhint-disable-next-line no-empty-blocks + function proposeAccusationVoteValidity(uint256) external pure {} + + // solhint-disable-next-line no-empty-blocks + function commitAccusationVoteValidity(uint256) external pure {} + + // solhint-disable-next-line no-empty-blocks + function cancelAccusationVoteValidityProposal() external pure {} + // solhint-disable-next-line no-empty-blocks function setSortitionSubmissionWindow(uint256) external pure {} @@ -163,6 +224,18 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { return false; } + function canonicalCommitteeNodeAt( + uint256 e3Id, + uint256 partyId + ) external view returns (address) { + address[] storage nodes = _committeeNodes[e3Id]; + require( + partyId < nodes.length, + PartyIdOutOfBounds(partyId, nodes.length) + ); + return nodes[partyId]; + } + function getActiveCommitteeNodes( uint256 ) external pure returns (address[] memory nodes, uint256[] memory scores) { @@ -214,6 +287,7 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { uint256, bytes calldata, bytes32, + bytes calldata, bytes calldata ) external pure {} // solhint-disable-line no-empty-blocks @@ -228,6 +302,20 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { return bytes32(0); } + function getDkgAnchors( + uint256 + ) + external + pure + returns ( + uint256[] memory partyIds, + bytes32[] memory skAggCommits, + bytes32[] memory esmAggCommits + ) + { + return (partyIds, skAggCommits, esmAggCommits); + } + function root() external pure returns (uint256) { return 0; } @@ -254,6 +342,22 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { return 0; } + function accusationVoteValidity() external pure returns (uint256) { + return 30 minutes; + } + + // solhint-disable-next-line no-empty-blocks + function setAccusationVoteValidity(uint256) external pure {} + + // solhint-disable-next-line no-empty-blocks + function proposeAccusationVoteValidity(uint256) external pure {} + + // solhint-disable-next-line no-empty-blocks + function commitAccusationVoteValidity(uint256) external pure {} + + // solhint-disable-next-line no-empty-blocks + function cancelAccusationVoteValidityProposal() external pure {} + // solhint-disable-next-line no-empty-blocks function setSortitionSubmissionWindow(uint256) external pure {} @@ -289,6 +393,13 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { return false; } + function canonicalCommitteeNodeAt( + uint256, + uint256 + ) external pure returns (address) { + return address(0); + } + function getActiveCommitteeNodes( uint256 ) external pure returns (address[] memory nodes, uint256[] memory scores) { diff --git a/packages/enclave-contracts/contracts/test/MockE3Program.sol b/packages/enclave-contracts/contracts/test/MockE3Program.sol index 972298f229..3c8d75e6f4 100644 --- a/packages/enclave-contracts/contracts/test/MockE3Program.sol +++ b/packages/enclave-contracts/contracts/test/MockE3Program.sol @@ -6,6 +6,7 @@ pragma solidity 0.8.28; import { IE3Program } from "../interfaces/IE3Program.sol"; +import { IEnclave } from "../interfaces/IEnclave.sol"; contract MockE3Program is IE3Program { error InvalidParams(bytes e3ProgramParams, bytes computeProviderParams); @@ -14,8 +15,19 @@ contract MockE3Program is IE3Program { bytes32 public constant ENCRYPTION_SCHEME_ID = keccak256("fhe.rs:BFV"); + /// @notice Optional Enclave contract — when set, `publishInput` forwards + /// `data` to `enclave.publishCiphertextOutput`, which is what the integration + /// tests rely on to trigger the ciphernode decryption pipeline. A real E3 + /// program would aggregate user inputs off-chain into a single ciphertext; + /// the mock short-circuits that step by treating the input as the ciphertext. + IEnclave public enclave; + mapping(uint256 e3Id => bytes32 paramsHash) public paramsHashes; + function setEnclave(IEnclave _enclave) external { + enclave = _enclave; + } + function validate( uint256 e3Id, uint256, @@ -33,10 +45,19 @@ contract MockE3Program is IE3Program { return ENCRYPTION_SCHEME_ID; } - function publishInput(uint256, bytes memory data) external pure { + function publishInput(uint256 e3Id, bytes memory data) external { if (data.length == 3) { revert InvalidInput(); } + if (address(enclave) != address(0)) { + // Test-only: external call to Enclave with no reentrancy guard. + // Deliberate — this contract is only deployed in integration tests + // and `enclave` is set via `setEnclave` to the trusted Enclave + // proxy. Do not copy this pattern into a production E3 program. + // Pass `data` as the proof too so `MockE3Program.verify` (which + // requires `proof.length > 0`) returns true. + enclave.publishCiphertextOutput(e3Id, data, data); + } } function verify( diff --git a/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol new file mode 100644 index 0000000000..2cd0a874ee --- /dev/null +++ b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +pragma solidity 0.8.28; + +import { ICiphernodeRegistry } from "../interfaces/ICiphernodeRegistry.sol"; +import { + IDkgFoldAttestationVerifier +} from "../interfaces/IDkgFoldAttestationVerifier.sol"; +import { DkgFoldAttestationLib } from "../lib/DkgFoldAttestationLib.sol"; + +/** + * @title DkgFoldAttestationVerifier + * @notice Stateless verifier for DKG fold attestations at committee publication. + */ +contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { + struct BundleData { + bytes32[] publicInputs; + DkgFoldAttestationLib.Attestation[] attestations; + DkgFoldAttestationLib.PartySlotBinding[] bindings; + uint256 h; + } + + /// @inheritdoc IDkgFoldAttestationVerifier + function verify( + address registry, + uint256 chainId, + uint256 e3Id, + bytes calldata proof, + bytes calldata dkgAttestationBundle + ) + external + view + returns ( + uint256[] memory partyIds, + bytes32[] memory skAggCommits, + bytes32[] memory esmAggCommits + ) + { + BundleData memory data = _loadBundle(proof, dkgAttestationBundle); + return _fillAnchors(registry, chainId, e3Id, data); + } + + function _loadBundle( + bytes calldata proof, + bytes calldata dkgAttestationBundle + ) private view returns (BundleData memory data) { + try this.decodeProofPublicInputs(proof) returns ( + bytes32[] memory publicInputs + ) { + data.publicInputs = publicInputs; + } catch { + revert ICiphernodeRegistry.InvalidFoldAttestation(); + } + data.h = _honestPartyCount(data.publicInputs); + // Defense in depth: require the public-inputs `partyId` slots + // (`publicInputs[2..2+h]`) to be strictly ascending. The zk circuit + // already enforces this, but rejecting duplicates here prevents two + // bindings from resolving to the same slot in `_partySlot` (which + // would silently overwrite each other in `partyIdsOut` etc.). + for (uint256 k = 1; k < data.h; k++) { + require( + uint256(data.publicInputs[2 + k]) > + uint256(data.publicInputs[2 + k - 1]), + ICiphernodeRegistry.InvalidFoldAttestation() + ); + } + try this.decodeAttestationBundle(dkgAttestationBundle) returns ( + DkgFoldAttestationLib.Attestation[] memory attestations, + DkgFoldAttestationLib.PartySlotBinding[] memory bindings + ) { + data.attestations = attestations; + data.bindings = bindings; + } catch { + revert ICiphernodeRegistry.InvalidFoldAttestation(); + } + if ( + data.attestations.length != data.bindings.length || + data.bindings.length != data.h + ) { + revert ICiphernodeRegistry.AttestationBindingCountMismatch(); + } + } + + /// @notice Exposed only for guarded decode in `_loadBundle`. + function decodeProofPublicInputs( + bytes calldata proof + ) external pure returns (bytes32[] memory publicInputs) { + (, publicInputs) = abi.decode(proof, (bytes, bytes32[])); + } + + /// @notice Exposed only for guarded decode in `_loadBundle`. + function decodeAttestationBundle( + bytes calldata dkgAttestationBundle + ) + external + pure + returns ( + DkgFoldAttestationLib.Attestation[] memory attestations, + DkgFoldAttestationLib.PartySlotBinding[] memory bindings + ) + { + (attestations, bindings) = abi.decode( + dkgAttestationBundle, + ( + DkgFoldAttestationLib.Attestation[], + DkgFoldAttestationLib.PartySlotBinding[] + ) + ); + } + + function _fillAnchors( + address registry, + uint256 chainId, + uint256 e3Id, + BundleData memory data + ) + private + view + returns ( + uint256[] memory partyIdsOut, + bytes32[] memory skAggOut, + bytes32[] memory esmAggOut + ) + { + partyIdsOut = new uint256[](data.h); + skAggOut = new bytes32[](data.h); + esmAggOut = new bytes32[](data.h); + + for (uint256 i = 0; i < data.h; i++) { + _applyBinding( + registry, + chainId, + e3Id, + data, + i, + partyIdsOut, + skAggOut, + esmAggOut + ); + } + } + + function _applyBinding( + address registry, + uint256 chainId, + uint256 e3Id, + BundleData memory data, + uint256 i, + uint256[] memory partyIdsOut, + bytes32[] memory skAggOut, + bytes32[] memory esmAggOut + ) private view { + if (i > 0) { + require( + data.bindings[i].partyId > data.bindings[i - 1].partyId, + ICiphernodeRegistry.InvalidFoldAttestation() + ); + require( + data.attestations[i].partyId > data.attestations[i - 1].partyId, + ICiphernodeRegistry.InvalidFoldAttestation() + ); + } + + (uint256 slot, bytes32 sk, bytes32 esm) = _verifyBinding( + registry, + chainId, + e3Id, + data, + data.bindings[i], + data.attestations[i] + ); + + partyIdsOut[slot] = data.bindings[i].partyId; + skAggOut[slot] = sk; + esmAggOut[slot] = esm; + } + + function _honestPartyCount( + bytes32[] memory publicInputs + ) private pure returns (uint256 h) { + require( + publicInputs.length >= 6 && (publicInputs.length - 6) % 3 == 0, + ICiphernodeRegistry.InvalidFoldAttestation() + ); + h = (publicInputs.length - 6) / 3; + // Defense in depth: the BFV pk-verifier already rejects `h == 0`, but + // a zero-honest-party proof would otherwise pass this verifier with no + // attestations to check and write empty anchors to the registry. + require(h > 0, ICiphernodeRegistry.InvalidFoldAttestation()); + } + + function _verifyBinding( + address registry, + uint256 chainId, + uint256 e3Id, + BundleData memory data, + DkgFoldAttestationLib.PartySlotBinding memory binding, + DkgFoldAttestationLib.Attestation memory att + ) private view returns (uint256 slot, bytes32 skCommit, bytes32 esmCommit) { + // Canonical-slot binding: `binding.node` must equal the canonical + // operator at index `partyId` of the finalized committee — i.e. + // `canonicalCommitteeNodeAt(e3Id, partyId)`, which returns + // `topNodes[partyId]` in address-ascending order. `partyId` is the + // canonical sortition slot id, so this rejects any binding pointing + // to an operator who is not the canonical occupant of that slot, + // even one who is otherwise an active committee member. Combined + // with the EIP-712 `ecrecover` check below, the attestation is + // bound to *both* the right address and the right canonical slot. + require( + ICiphernodeRegistry(registry).canonicalCommitteeNodeAt( + e3Id, + binding.partyId + ) == binding.node, + ICiphernodeRegistry.InvalidFoldAttestation() + ); + require( + ICiphernodeRegistry(registry).isCommitteeMemberActive( + e3Id, + binding.node + ), + ICiphernodeRegistry.InvalidFoldAttestation() + ); + + require( + att.partyId == binding.partyId, + ICiphernodeRegistry.InvalidFoldAttestation() + ); + + address signer = DkgFoldAttestationLib.recoverSigner( + chainId, + address(this), + e3Id, + att + ); + require( + signer == binding.node, + ICiphernodeRegistry.InvalidFoldAttestation() + ); + + uint256 partyIdOffset = 2; + uint256 skOffset = 5 + data.h; + uint256 esmOffset = 5 + (2 * data.h); + + slot = _partySlot( + data.publicInputs, + partyIdOffset, + data.h, + binding.partyId + ); + + require( + data.publicInputs[skOffset + slot] == att.skAggCommit && + data.publicInputs[esmOffset + slot] == att.esmAggCommit, + ICiphernodeRegistry.InvalidFoldAttestation() + ); + + return (slot, att.skAggCommit, att.esmAggCommit); + } + + function _partySlot( + bytes32[] memory publicInputs, + uint256 partyIdOffset, + uint256 h, + uint256 partyId + ) private pure returns (uint256 slot) { + for (uint256 k = 0; k < h; k++) { + if (uint256(publicInputs[partyIdOffset + k]) == partyId) { + return k; + } + } + revert ICiphernodeRegistry.PartyIdNotInProof(); + } +} diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol index df2d85f544..44e71baccb 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol @@ -156,7 +156,7 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { function _verifyPlaintextHash( bytes32[] memory publicInputs, - bytes32 plaintextOutputHash + bytes32 expected ) internal view returns (bool) { uint256 offset = expectedPublicInputsLen - MESSAGE_COEFFS_COUNT; bytes memory plaintext = new bytes(MESSAGE_COEFFS_COUNT * 8); @@ -166,6 +166,6 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { plaintext[i * 8 + j] = bytes1(uint8(coeff >> (j * 8))); } } - return keccak256(plaintext) == plaintextOutputHash; + return keccak256(plaintext) == expected; } } diff --git a/packages/enclave-contracts/deployed_contracts.json b/packages/enclave-contracts/deployed_contracts.json index af108bf425..2454b08a53 100644 --- a/packages/enclave-contracts/deployed_contracts.json +++ b/packages/enclave-contracts/deployed_contracts.json @@ -126,21 +126,21 @@ }, "localhost": { "PoseidonT3": { - "blockNumber": 5, + "blockNumber": 6, "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "MockUSDC": { "constructorArgs": { "initialSupply": "1000000" }, - "blockNumber": 6, + "blockNumber": 7, "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" }, "EnclaveToken": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 7, + "blockNumber": 8, "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" }, "EnclaveTicketToken": { @@ -149,7 +149,7 @@ "registry": "0x0000000000000000000000000000000000000001", "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 9, + "blockNumber": 10, "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" }, "SlashingManager": { @@ -157,8 +157,97 @@ "initialDelay": "172800", "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 10, + "blockNumber": 11, "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" + }, + "CiphernodeRegistryOwnable": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "submissionWindow": "10" + }, + "proxyRecords": { + "initData": "0xcd6dc687000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000000a", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "proxyAdminAddress": "0x9bd03768a7DCc129555dE410FF8E85528A4F88b5", + "implementationAddress": "0x0165878A594ca255338adfa4d48449f69242Eb8F" + }, + "blockNumber": 11, + "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" + }, + "BondingRegistry": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "ticketToken": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9", + "licenseToken": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", + "registry": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "slashedFundsTreasury": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "ticketPrice": "10000000", + "licenseRequiredBond": "100000000000000000000", + "minTicketBalance": "1", + "exitDelay": "604800" + }, + "proxyRecords": { + "initData": "0x7333fa82000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000dc64a140aa3e981100a9beca4e685f962f0cf6c90000000000000000000000009fe46736679d2d9a65f0992f2272de9f3c7fa6e0000000000000000000000000a513e6e4b8f2a923d98304ec87f64353c4d5c853000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000009896800000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000093a80", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318", + "proxyAdminAddress": "0x8aCd85898458400f7Db866d53FCFF6f0D49741FF", + "implementationAddress": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" + }, + "blockNumber": 12, + "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + }, + "Enclave": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "registry": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "bondingRegistry": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318", + "e3RefundManager": "0x0000000000000000000000000000000000000001", + "feeToken": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "maxDuration": "2592000", + "timeoutConfig": "{\"committeeFormationWindow\":3600,\"dkgWindow\":7200,\"computeWindow\":86400,\"decryptionWindow\":3600}" + }, + "proxyRecords": { + "initData": "0x4d600e5d000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000a513e6e4b8f2a923d98304ec87f64353c4d5c8530000000000000000000000008a791620dd6260079bf849dc5567adc3f2fdc3180000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f05120000000000000000000000000000000000000000000000000000000000278d000000000000000000000000000000000000000000000000000000000000001c2000000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000e10", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", + "proxyAdminAddress": "0x8dAF17A20c9DBA35f005b6324F493785D239719d", + "implementationAddress": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" + }, + "blockNumber": 15, + "address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" + }, + "E3RefundManager": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "enclave": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", + "treasury": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "proxyRecords": { + "initData": "0xc0c53b8b000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000b7f8bc63bbcad18155201308c8f3540b07f84f5e000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82", + "proxyAdminAddress": "0x32467b43BFa67273FC7dDda0999Ee9A12F2AaA08", + "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" + }, + "blockNumber": 17, + "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" + }, + "MockComputeProvider": { + "blockNumber": 19, + "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" + }, + "MockDecryptionVerifier": { + "blockNumber": 20, + "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9" + }, + "MockPkVerifier": { + "blockNumber": 21, + "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" + }, + "MockE3Program": { + "blockNumber": 22, + "address": "0x851356ae760d987E095750cCeb3bC6014560891C" } } } \ No newline at end of file diff --git a/packages/enclave-contracts/hardhat.config.ts b/packages/enclave-contracts/hardhat.config.ts index 43aecb61c6..89be5d8787 100644 --- a/packages/enclave-contracts/hardhat.config.ts +++ b/packages/enclave-contracts/hardhat.config.ts @@ -29,7 +29,7 @@ import { publishPlaintext, requestCommittee, } from "./tasks/enclave"; -import { publishInput } from "./tasks/program"; +import { publishInput, setMockProgramEnclave } from "./tasks/program"; import { cleanDeploymentsTask } from "./tasks/utils"; dotenv.config(); @@ -102,6 +102,7 @@ const config: HardhatUserConfig = { publishCommittee, getPlaintextOutput, publishInput, + setMockProgramEnclave, enableE3, cleanDeploymentsTask, updateSubmissionWindow, diff --git a/packages/enclave-contracts/ignition/modules/dkgFoldAttestationVerifier.ts b/packages/enclave-contracts/ignition/modules/dkgFoldAttestationVerifier.ts new file mode 100644 index 0000000000..5e29344b88 --- /dev/null +++ b/packages/enclave-contracts/ignition/modules/dkgFoldAttestationVerifier.ts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; + +export default buildModule("DkgFoldAttestationVerifier", (m) => { + const dkgFoldAttestationVerifier = m.contract("DkgFoldAttestationVerifier"); + return { dkgFoldAttestationVerifier }; +}) as any; diff --git a/packages/enclave-contracts/package.json b/packages/enclave-contracts/package.json index 3e51312371..9a4d6c7d2e 100644 --- a/packages/enclave-contracts/package.json +++ b/packages/enclave-contracts/package.json @@ -158,7 +158,8 @@ "compile:ts": "tsc", "coverage": "pnpm test --coverage", "deploy": "hardhat run scripts/run.ts", - "deploy:mocks": "export DEPLOY_MOCKS=true && pnpm clean:deployments && pnpm run deploy", + "deploy:mocks": "export DEPLOY_MOCKS=true && pnpm clean:deployments && hardhat run scripts/run.ts --network localhost", + "configure:slashing-policies": "hardhat run scripts/runConfigureSlashingPolicies.ts --network localhost", "deploy:verifiers": "hardhat run scripts/deployVerifiers.ts", "upgrade:enclave": "hardhat run scripts/upgrade/enclave.ts", "upgrade:bondingRegistry": "hardhat run scripts/upgrade/bondingRegistry.ts", @@ -187,7 +188,8 @@ "prerelease": "pnpm clean && pnpm compile && pnpm typechain", "release": "pnpm publish", "verify:contracts": "hardhat run scripts/runVerification.ts", - "updateSubmissionWindow": "hardhat ciphernode:window" + "updateSubmissionWindow": "hardhat ciphernode:window", + "utils:sync-integration-config": "hardhat run scripts/syncIntegrationConfig.ts --network localhost" }, "dependencies": { "@openzeppelin/contracts-upgradeable": "^5.0.2", diff --git a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts index 6043dae8c8..4bdce87c1f 100644 --- a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts +++ b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts @@ -4,6 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. import { network } from "hardhat"; +import { execFileSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; @@ -12,12 +13,136 @@ import { BFV_DKG_H, BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS, BFV_THRESHOLD_T, + REPO_ROOT, bfvDecCommitteeHashIndices, bfvDkgCommitteeHashIndices, committeeHashFromLimbs, readVkRecursiveHash, } from "./utils"; +const CANONICAL_BFV_PRESET = "insecure-512"; +const COMMITTED_HONK_DIR = path.join( + REPO_ROOT, + "packages/enclave-contracts/contracts/verifiers/bfv/honk", +); + +function presetFromFoldedArtifact(artifact: unknown): string | undefined { + if (!artifact || typeof artifact !== "object") { + return undefined; + } + const doc = artifact as Record; + const pick = (value: unknown): string | undefined => { + if (typeof value !== "string") return undefined; + const trimmed = value.trim(); + return trimmed.length > 0 ? trimmed : undefined; + }; + + const direct = pick(doc.preset) ?? pick(doc.bfv_preset_subdir); + if (direct) return direct; + + const benchmarkConfig = doc.benchmark_config; + if (benchmarkConfig && typeof benchmarkConfig === "object") { + const cfg = benchmarkConfig as Record; + const fromConfig = pick(cfg.bfv_preset_subdir) ?? pick(cfg.preset); + if (fromConfig) return fromConfig; + } + + return undefined; +} + +/** Prefer preset embedded in replayed folded/summary JSON, then env / active preset. */ +function readBenchmarkPreset(foldedArtifact?: unknown): string { + const fromArtifact = presetFromFoldedArtifact(foldedArtifact); + if (fromArtifact) return fromArtifact; + + const fromEnv = process.env.BENCHMARK_PRESET?.trim(); + if (fromEnv) return fromEnv; + const activePath = path.join(REPO_ROOT, "circuits/bin/.active-preset.json"); + if (!fs.existsSync(activePath)) { + return CANONICAL_BFV_PRESET; + } + try { + const active = JSON.parse(fs.readFileSync(activePath, "utf8")) as { + preset?: string; + }; + return active.preset ?? CANONICAL_BFV_PRESET; + } catch { + return CANONICAL_BFV_PRESET; + } +} + +/** + * Committed Honk `.sol` files embed the insecure-512 aggregator VK. Secure benchmark + * proofs need verifiers generated from the active circuits/bin preset. + */ +function ensureHonkVerifierContractDir(preset: string): string { + if (preset === CANONICAL_BFV_PRESET) { + return COMMITTED_HONK_DIR; + } + const benchDir = path.join(COMMITTED_HONK_DIR, ".benchmark", preset); + fs.mkdirSync(benchDir, { recursive: true }); + console.log( + `[benchmarkGasFromRaw] Generating ${preset} Honk verifiers into ${benchDir}...`, + ); + execFileSync( + "pnpm", + [ + "generate:verifiers", + "--circuits", + "dkg_aggregator,decryption_aggregator", + "--no-compile", + "--write", + "--preset", + preset, + "--output-dir", + benchDir, + ], + { cwd: REPO_ROOT, stdio: "inherit" }, + ); + // Hardhat does not pick up freshly written .sol under honk/.benchmark/ until compile. + execFileSync("pnpm", ["hardhat", "compile"], { + cwd: path.join(REPO_ROOT, "packages/enclave-contracts"), + stdio: "inherit", + }); + return benchDir; +} + +/** Hardhat `project/` source path for a generated Honk verifier file. */ +function honkContractSource(honkDir: string, name: string): string { + const rel = path.relative( + path.join(REPO_ROOT, "packages/enclave-contracts"), + path.join(honkDir, `${name}.sol`), + ); + return rel.split(path.sep).join("/"); +} + +async function deployHonkAggregator( + ethersLib: Awaited>["ethers"], + honkDir: string, + contractName: "DkgAggregatorVerifier" | "DecryptionAggregatorVerifier", +): Promise { + const solSource = honkContractSource(honkDir, contractName); + const libKey = `project/${solSource}:ZKTranscriptLib`; + const libFactory = await ethersLib.getContractFactory( + `${solSource}:ZKTranscriptLib`, + ); + const lib = await libFactory.deploy(); + await lib.waitForDeployment(); + const libAddress = await lib.getAddress(); + + const aggFactory = await ethersLib.getContractFactory( + `${solSource}:${contractName}`, + { + libraries: { + [libKey]: libAddress, + }, + }, + ); + const agg = await aggFactory.deploy(); + await agg.waitForDeployment(); + return agg.getAddress(); +} + function findRawJson(rawDir: string, fragment: string): any { const entries = fs.readdirSync(rawDir).filter((f) => f.endsWith(".json")); for (const f of entries) { @@ -87,18 +212,47 @@ async function main() { } const { ethers } = await network.connect(); + const [benchmarkSigner] = await ethers.getSigners(); + // e3Id / committeeRoot / sortedNodes are forward-compat params; wrappers do not + // bind them yet (see BfvPkVerifier / BfvDecryptionVerifier). Any fixed values + // yield representative verify gas for the Honk + wrapper path. + const benchmarkE3Id = 1n; + const benchmarkCommitteeRoot = BigInt( + ethers.id("benchmark-gas-committee-root"), + ); + const benchmarkSortedNodes = [benchmarkSigner.address]; + const benchmarkCiphertextHash = ethers.ZeroHash; + const benchmarkCommitteePublicKey = ethers.ZeroHash; let dkgProofHex: string | undefined; let dkgPublicHex: string | undefined; let decProofHex: string | undefined; let decPublicHex: string | undefined; + let foldedDoc: unknown; if (foldedPath && fs.existsSync(foldedPath)) { - const folded = JSON.parse(fs.readFileSync(foldedPath, "utf8")); - dkgProofHex = folded?.dkg_aggregator?.proof_hex; - dkgPublicHex = folded?.dkg_aggregator?.public_inputs_hex; - decProofHex = folded?.decryption_aggregator?.proof_hex; - decPublicHex = folded?.decryption_aggregator?.public_inputs_hex; + const raw = fs.readFileSync(foldedPath, "utf8").trim(); + if (!raw) { + console.warn( + `[benchmarkGasFromRaw] ${foldedPath} is empty — integration test likely failed before exporting folded proofs`, + ); + } else { + foldedDoc = JSON.parse(raw); + const artifacts = + (foldedDoc as { folded_artifacts?: unknown }).folded_artifacts ?? + foldedDoc; + const proofBundle = artifacts as { + dkg_aggregator?: { proof_hex?: string; public_inputs_hex?: string }; + decryption_aggregator?: { + proof_hex?: string; + public_inputs_hex?: string; + }; + }; + dkgProofHex = proofBundle?.dkg_aggregator?.proof_hex; + dkgPublicHex = proofBundle?.dkg_aggregator?.public_inputs_hex; + decProofHex = proofBundle?.decryption_aggregator?.proof_hex; + decPublicHex = proofBundle?.decryption_aggregator?.public_inputs_hex; + } } else { const dkgRaw = findRawJson(rawDir, "threshold_pk_aggregation"); const decRaw = findRawJson( @@ -112,8 +266,16 @@ async function main() { } if (!dkgProofHex || !dkgPublicHex || !decProofHex || !decPublicHex) { + const missing = [ + !dkgProofHex && "dkg proof", + !dkgPublicHex && "dkg public inputs", + !decProofHex && "decryption proof", + !decPublicHex && "decryption public inputs", + ] + .filter(Boolean) + .join(", "); throw new Error( - "Missing proof/public_inputs hex fields in raw benchmark JSON", + `[benchmarkGasFromRaw] Missing benchmark proofs (${missing}); run test_trbfv_actor successfully first. outputPath=${outputPath}`, ); } @@ -162,38 +324,24 @@ async function main() { const abiCoder = ethers.AbiCoder.defaultAbiCoder(); - const libFactory = await ethers.getContractFactory( - "contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:ZKTranscriptLib", - ); - const zkTranscriptLib = await libFactory.deploy(); - await zkTranscriptLib.waitForDeployment(); - const zkTranscriptLibAddress = await zkTranscriptLib.getAddress(); + const benchmarkPreset = readBenchmarkPreset(foldedDoc); + const honkDir = ensureHonkVerifierContractDir(benchmarkPreset); + if (benchmarkPreset !== CANONICAL_BFV_PRESET) { + console.log( + `[benchmarkGasFromRaw] Using preset ${benchmarkPreset} Honk verifiers (not committed insecure-512 .sol).`, + ); + } - const dkgAggFactory = await ethers.getContractFactory( + const dkgAggAddress = await deployHonkAggregator( + ethers, + honkDir, "DkgAggregatorVerifier", - { - libraries: { - "project/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:ZKTranscriptLib": - zkTranscriptLibAddress, - }, - }, ); - const dkgAgg = await dkgAggFactory.deploy(); - await dkgAgg.waitForDeployment(); - const dkgAggAddress = await dkgAgg.getAddress(); - - const decAggFactory = await ethers.getContractFactory( + const decAggAddress = await deployHonkAggregator( + ethers, + honkDir, "DecryptionAggregatorVerifier", - { - libraries: { - "project/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol:ZKTranscriptLib": - zkTranscriptLibAddress, - }, - }, ); - const decAgg = await decAggFactory.deploy(); - await decAgg.waitForDeployment(); - const decAggAddress = await decAgg.getAddress(); const bfvPk = await ( await ethers.getContractFactory("BfvPkVerifier") @@ -220,6 +368,9 @@ async function main() { dkgPublicInputs[DKG_COMMITTEE_HASH_IDX.lo], ); const dkgOk = await bfvPk.verify.staticCall( + benchmarkE3Id, + benchmarkCommitteeRoot, + benchmarkSortedNodes, pkCommitment, dkgCommitteeHash, dkgEncodedProof, @@ -230,6 +381,9 @@ async function main() { ); } const dkgGas = await bfvPk.verify.estimateGas( + benchmarkE3Id, + benchmarkCommitteeRoot, + benchmarkSortedNodes, pkCommitment, dkgCommitteeHash, dkgEncodedProof, @@ -260,6 +414,11 @@ async function main() { decPublicInputs[DEC_COMMITTEE_HASH_IDX.lo], ); const decOk = await bfvDec.verify.staticCall( + benchmarkE3Id, + benchmarkCommitteeRoot, + benchmarkSortedNodes, + benchmarkCiphertextHash, + benchmarkCommitteePublicKey, plaintextHash, decCommitteeHash, decEncodedProof, @@ -270,6 +429,11 @@ async function main() { ); } const decGas = await bfvDec.verify.estimateGas( + benchmarkE3Id, + benchmarkCommitteeRoot, + benchmarkSortedNodes, + benchmarkCiphertextHash, + benchmarkCommitteePublicKey, plaintextHash, decCommitteeHash, decEncodedProof, @@ -281,6 +445,7 @@ async function main() { dec: Number(decGas), }, source: "benchmark_raw_artifacts", + bfv_preset: benchmarkPreset, }; fs.writeFileSync(outputPath, JSON.stringify(output, null, 2)); } diff --git a/packages/enclave-contracts/scripts/cleanIgnitionState.ts b/packages/enclave-contracts/scripts/cleanIgnitionState.ts index e9995ab99e..d2e130ace5 100644 --- a/packages/enclave-contracts/scripts/cleanIgnitionState.ts +++ b/packages/enclave-contracts/scripts/cleanIgnitionState.ts @@ -3,8 +3,7 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -import fs from "fs"; -import path from "path"; +import { cleanLocalDeployments } from "./utils"; /** * Cleans deployment records for a specific network from deployed_contracts.json @@ -12,26 +11,8 @@ import path from "path"; * @param networkName - The network name (e.g., "localhost", "hardhat") */ export const cleanDeploymentRecords = (networkName: string): void => { - const deploymentsFile = path.join(process.cwd(), "deployed_contracts.json"); - - if (!fs.existsSync(deploymentsFile)) { - return; - } - - try { - const deployments = JSON.parse(fs.readFileSync(deploymentsFile, "utf8")); - - if (deployments[networkName]) { - console.log( - `Cleaning deployment records for network '${networkName}'...`, - ); - delete deployments[networkName]; - fs.writeFileSync(deploymentsFile, JSON.stringify(deployments, null, 2)); - console.log(`Cleaned deployment records for '${networkName}'`); - } - } catch (error) { - console.warn("Failed to clean deployment records:", error); - } + cleanLocalDeployments(networkName); + console.log(`Cleaned deployment records for local network '${networkName}'`); }; /** diff --git a/packages/enclave-contracts/scripts/configureLocalSlashingPolicies.ts b/packages/enclave-contracts/scripts/configureLocalSlashingPolicies.ts new file mode 100644 index 0000000000..df221f8593 --- /dev/null +++ b/packages/enclave-contracts/scripts/configureLocalSlashingPolicies.ts @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import type { ethers as EthersTypes } from "ethers"; +import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; + +import { + SlashingManager, + SlashingManager__factory as SlashingManagerFactory, +} from "../types"; +import type { ISlashingManager } from "../types/contracts/interfaces/ISlashingManager"; +import { getDeploymentChain, readDeploymentArgs } from "./utils"; + +/** Proof types 0–7: DKG-stage proofs (C0–C4). */ +const DKG_PROOF_TYPES = [0, 1, 2, 3, 4, 5, 6, 7] as const; +/** Proof types 8–10: aggregation / decryption (C5–C7). */ +const DECRYPTION_PROOF_TYPES = [8, 9, 10] as const; + +/** `IEnclave.FailureReason.DKGInvalidShares` */ +const FAILURE_REASON_DKG_INVALID_SHARES = 4; +/** `IEnclave.FailureReason.DecryptionInvalidShares` */ +const FAILURE_REASON_DECRYPTION_INVALID_SHARES = 11; + +function slashReasonForProofType( + ethers: typeof EthersTypes, + proofType: number, +): string { + return ethers.keccak256(ethers.solidityPacked(["uint256"], [proofType])); +} + +function localAttestationSlashPolicy( + ethers: typeof EthersTypes, + failureReason: number, +): ISlashingManager.SlashPolicyStruct { + // Lane A (`proposeSlash`): committee attestation is verified in SlashingManager; + // `proofVerifier` is unused (reserved for future ZK verifier wiring). ZeroAddress is intentional. + return { + ticketPenalty: ethers.parseUnits("10", 6), + licensePenalty: ethers.parseEther("50"), + requiresProof: true, + proofVerifier: ethers.ZeroAddress, + banNode: false, + appealWindow: 0, + enabled: true, + affectsCommittee: true, + failureReason, + }; +} + +/** + * Enables Lane A (`proposeSlash`) policies for all `ProofType` values (0–10). + * Local dev deploys omit this by default, which causes `SlashReasonDisabled` reverts. + */ +export async function configureLocalSlashingPolicies( + hre: HardhatRuntimeEnvironment, + slashingManager?: SlashingManager, +): Promise { + const { ethers } = await hre.network.connect(); + const chain = getDeploymentChain(hre); + + const contract = + slashingManager ?? + SlashingManagerFactory.connect( + readDeploymentArgs("SlashingManager", chain)?.address ?? + (() => { + throw new Error( + "SlashingManager address not found; deploy contracts first", + ); + })(), + (await ethers.getSigners())[0], + ); + + console.log( + "Configuring local SlashingManager policies (proof types 0–10)...", + ); + + for (const proofType of DKG_PROOF_TYPES) { + const reason = slashReasonForProofType(ethers, proofType); + const tx = await contract.setSlashPolicy( + reason, + localAttestationSlashPolicy(ethers, FAILURE_REASON_DKG_INVALID_SHARES), + ); + await tx.wait(); + console.log(` proofType ${proofType} (DKG) -> ${reason}`); + } + + for (const proofType of DECRYPTION_PROOF_TYPES) { + const reason = slashReasonForProofType(ethers, proofType); + const tx = await contract.setSlashPolicy( + reason, + localAttestationSlashPolicy( + ethers, + FAILURE_REASON_DECRYPTION_INVALID_SHARES, + ), + ); + await tx.wait(); + console.log(` proofType ${proofType} (decryption) -> ${reason}`); + } + + console.log("Local slashing policies configured."); +} diff --git a/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts b/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts index 761a8f8333..964f3cbd2a 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts @@ -10,7 +10,11 @@ import { BondingRegistry__factory as BondingRegistryFactory, } from "../../types"; import { getProxyAdmin, verifyProxyAdminOwner } from "../proxy"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; /** * The arguments for the deployAndSaveBondingRegistry function @@ -49,7 +53,7 @@ export const deployAndSaveBondingRegistry = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = hre.globalOptions.network; + const chain = getDeploymentChain(hre); const preDeployedArgs = readDeploymentArgs("BondingRegistry", chain); diff --git a/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts b/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts index 06f3507fbb..1c6dfede93 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts @@ -10,7 +10,11 @@ import { CiphernodeRegistryOwnable__factory as CiphernodeRegistryOwnableFactory, } from "../../types"; import { getProxyAdmin, verifyProxyAdminOwner } from "../proxy"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; /** * The arguments for the deployAndSaveCiphernodeRegistryOwnable function @@ -37,7 +41,7 @@ export const deployAndSaveCiphernodeRegistryOwnable = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = hre.globalOptions.network; + const chain = getDeploymentChain(hre); const preDeployedArgs = readDeploymentArgs( "CiphernodeRegistryOwnable", diff --git a/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts b/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts new file mode 100644 index 0000000000..61af98c3f1 --- /dev/null +++ b/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; + +import { + DkgFoldAttestationVerifier, + DkgFoldAttestationVerifier__factory as DkgFoldAttestationVerifierFactory, +} from "../../types"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; + +export const deployAndSaveDkgFoldAttestationVerifier = async ( + hre: HardhatRuntimeEnvironment, +): Promise<{ + dkgFoldAttestationVerifier: DkgFoldAttestationVerifier; +}> => { + const { ethers } = await hre.network.connect(); + const [signer] = await ethers.getSigners(); + const chain = getDeploymentChain(hre); + + const existing = readDeploymentArgs("DkgFoldAttestationVerifier", chain); + if (existing?.address) { + console.log( + ` DkgFoldAttestationVerifier already deployed at ${existing.address}`, + ); + const dkgFoldAttestationVerifier = + DkgFoldAttestationVerifierFactory.connect(existing.address, signer); + return { dkgFoldAttestationVerifier }; + } + + const dkgFoldAttestationVerifier = + await new DkgFoldAttestationVerifierFactory(signer).deploy(); + await dkgFoldAttestationVerifier.waitForDeployment(); + const address = await dkgFoldAttestationVerifier.getAddress(); + const blockNumber = await ethers.provider.getBlockNumber(); + + storeDeploymentArgs( + { address, blockNumber }, + "DkgFoldAttestationVerifier", + chain, + ); + console.log(` DkgFoldAttestationVerifier deployed to: ${address}`); + + return { dkgFoldAttestationVerifier }; +}; diff --git a/packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts b/packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts index ce7870b7fd..245692d70d 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts @@ -9,7 +9,11 @@ import { EnclaveTicketToken, EnclaveTicketToken__factory as EnclaveTicketTokenFactory, } from "../../types"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; /** * The arguments for the deployAndSaveEnclaveTicketToken function @@ -36,7 +40,7 @@ export const deployAndSaveEnclaveTicketToken = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = hre.globalOptions.network; + const chain = getDeploymentChain(hre); const preDeployedArgs = readDeploymentArgs("EnclaveTicketToken", chain); diff --git a/packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts b/packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts index 51603c4851..1db61357a4 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts @@ -9,7 +9,11 @@ import { EnclaveToken, EnclaveToken__factory as EnclaveTokenFactory, } from "../../types"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; /** * The arguments for the deployAndSaveEnclaveToken function @@ -57,7 +61,7 @@ export const deployAndSaveEnclaveToken = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = hre.globalOptions.network; + const chain = getDeploymentChain(hre); const preDeployedArgs = readDeploymentArgs("EnclaveToken", chain); diff --git a/packages/enclave-contracts/scripts/deployAndSave/mockPkVerifier.ts b/packages/enclave-contracts/scripts/deployAndSave/mockPkVerifier.ts index fd4ae67363..77a2cfa5f2 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/mockPkVerifier.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/mockPkVerifier.ts @@ -9,7 +9,7 @@ import { MockPkVerifier, MockPkVerifier__factory as MockPkVerifierFactory, } from "../../types"; -import { storeDeploymentArgs } from "../utils"; +import { getDeploymentChain, storeDeploymentArgs } from "../utils"; export const deployAndSaveMockPkVerifier = async ( hre: HardhatRuntimeEnvironment, @@ -18,7 +18,7 @@ export const deployAndSaveMockPkVerifier = async ( }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = hre.globalOptions.network; + const chain = getDeploymentChain(hre); const pkVerifierFactory = await ethers.getContractFactory("MockPkVerifier"); const pkVerifier = await pkVerifierFactory.deploy(); diff --git a/packages/enclave-contracts/scripts/deployAndSave/mockProgram.ts b/packages/enclave-contracts/scripts/deployAndSave/mockProgram.ts index 48e66d71d2..d7202c1fee 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/mockProgram.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/mockProgram.ts @@ -9,7 +9,7 @@ import { MockE3Program, MockE3Program__factory as MockE3ProgramFactory, } from "../../types"; -import { storeDeploymentArgs } from "../utils"; +import { getDeploymentChain, storeDeploymentArgs } from "../utils"; interface MockProgramArgs { hre: HardhatRuntimeEnvironment; @@ -22,7 +22,7 @@ export const deployAndSaveMockProgram = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = hre.globalOptions.network; + const chain = getDeploymentChain(hre); const e3ProgramFactory = await ethers.getContractFactory("MockE3Program"); const e3Program = await e3ProgramFactory.deploy(); diff --git a/packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts b/packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts index 43f52c5e07..a4ad38e520 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts @@ -6,7 +6,11 @@ import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; import { MockUSDC, MockUSDC__factory as MockUSDCFactory } from "../../types"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; /** * The arguments for the deployAndSaveMockStableToken function @@ -29,7 +33,7 @@ export const deployAndSaveMockStableToken = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = hre.globalOptions.network; + const chain = getDeploymentChain(hre); const preDeployedArgs = readDeploymentArgs("MockUSDC", chain); diff --git a/packages/enclave-contracts/scripts/deployAndSave/poseidonT3.ts b/packages/enclave-contracts/scripts/deployAndSave/poseidonT3.ts index b301480e28..6cdaa1032a 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/poseidonT3.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/poseidonT3.ts @@ -6,7 +6,7 @@ import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; import poseidon from "poseidon-solidity"; -import { storeDeploymentArgs } from "../utils"; +import { getDeploymentChain, storeDeploymentArgs } from "../utils"; interface PoseidonT3ProxyDeployArgs { hre: HardhatRuntimeEnvironment; @@ -20,7 +20,7 @@ export const deployAndSavePoseidonT3 = async ({ hre, }: PoseidonT3ProxyDeployArgs): Promise => { const { ethers } = await hre.network.connect(); - const chain = hre.globalOptions.network; + const chain = getDeploymentChain(hre); // First check if the proxy exists if ((await ethers.provider.getCode(poseidon.proxy.address)) === "0x") { diff --git a/packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts b/packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts index 552486d568..ab70e85d50 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts @@ -9,7 +9,11 @@ import { SlashingManager, SlashingManager__factory as SlashingManagerFactory, } from "../../types"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; /** * The arguments for the deployAndSaveSlashingManager function @@ -40,7 +44,7 @@ export const deployAndSaveSlashingManager = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = hre.globalOptions.network; + const chain = getDeploymentChain(hre); const delay = initialDelay !== undefined ? BigInt(initialDelay) : DEFAULT_ADMIN_DELAY; diff --git a/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts b/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts index fb1449653e..9232a28964 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts @@ -8,7 +8,11 @@ import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; import path from "path"; import { fileURLToPath } from "url"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; const BFV_HONK_VERIFIER_DIR = "contracts/verifiers/bfv/honk"; const NPM_HONK_SOURCE_PREFIX = @@ -112,7 +116,7 @@ export const deployAndSaveVerifier = async ( zkTranscriptLibAddress: string, ): Promise<{ address: string }> => { const { ethers } = await hre.network.connect(); - const chain = hre.globalOptions.network; + const chain = getDeploymentChain(hre); // Check if already deployed const existing = readDeploymentArgs(contractName, chain); @@ -166,7 +170,7 @@ export const deployAndSaveAllVerifiers = async ( hre: HardhatRuntimeEnvironment, ): Promise => { const contractNames = discoverVerifierContracts(); - const chain = hre.globalOptions.network; + const chain = getDeploymentChain(hre); console.log(` Deploying to network: ${chain}`); if (contractNames.length === 0) { diff --git a/packages/enclave-contracts/scripts/deployEnclave.ts b/packages/enclave-contracts/scripts/deployEnclave.ts index e80bfcf625..76864f1085 100644 --- a/packages/enclave-contracts/scripts/deployEnclave.ts +++ b/packages/enclave-contracts/scripts/deployEnclave.ts @@ -7,10 +7,12 @@ import { ethers as ethersLib } from "ethers"; import hre from "hardhat"; import { autoCleanForLocalhost } from "./cleanIgnitionState"; +import { configureLocalSlashingPolicies } from "./configureLocalSlashingPolicies"; import { deployAndSaveBfvDecryptionVerifier } from "./deployAndSave/bfvDecryptionVerifier"; import { deployAndSaveBfvPkVerifier } from "./deployAndSave/bfvPkVerifier"; import { deployAndSaveBondingRegistry } from "./deployAndSave/bondingRegistry"; import { deployAndSaveCiphernodeRegistryOwnable } from "./deployAndSave/ciphernodeRegistryOwnable"; +import { deployAndSaveDkgFoldAttestationVerifier } from "./deployAndSave/dkgFoldAttestationVerifier"; import { deployAndSaveE3RefundManager } from "./deployAndSave/e3RefundManager"; import { deployAndSaveEnclave } from "./deployAndSave/enclave"; import { deployAndSaveEnclaveTicketToken } from "./deployAndSave/enclaveTicketToken"; @@ -152,6 +154,14 @@ export const deployEnclave = async ( const enclaveTokenAddress = await enclaveToken.getAddress(); console.log("EnclaveToken deployed to:", enclaveTokenAddress); + if (enclaveTokenAddress.toLowerCase() === feeTokenAddress.toLowerCase()) { + throw new Error( + "MockUSDC and EnclaveToken resolved to the same address. " + + "Start a fresh Anvil on http://127.0.0.1:8545 (e.g. `anvil --chain-id 31337`) " + + "and rerun deploy so token nonces advance separately.", + ); + } + console.log("Deploying EnclaveTicketToken..."); const { enclaveTicketToken } = await deployAndSaveEnclaveTicketToken({ baseToken: feeTokenAddress, @@ -268,6 +278,11 @@ export const deployEnclave = async ( console.log("Setting SlashingManager address in CiphernodeRegistry..."); await ciphernodeRegistry.setSlashingManager(slashingManagerAddress); + if (shouldDeployMocks) { + console.log("Configuring local SlashingManager slash policies..."); + await configureLocalSlashingPolicies(hre, slashingManager); + } + // H-24: SLASHER_ROLE must be granted explicitly. Without this, Lane B // (evidence-based) slash proposals are uncallable and there is no on-chain // path to penalise nodes for off-chain misbehaviour. Source the slasher @@ -443,6 +458,26 @@ export const deployEnclave = async ( } } + let dkgFoldAttestationVerifierAddress: string | undefined; + if (shouldHaveZKVerification) { + console.log("Deploying DkgFoldAttestationVerifier..."); + const { dkgFoldAttestationVerifier } = + await deployAndSaveDkgFoldAttestationVerifier(hre); + dkgFoldAttestationVerifierAddress = + await dkgFoldAttestationVerifier.getAddress(); + const currentVerifier = + await ciphernodeRegistry.dkgFoldAttestationVerifier(); + if (currentVerifier !== dkgFoldAttestationVerifierAddress) { + const tx = await ciphernodeRegistry.setInitialDkgFoldAttestationVerifier( + dkgFoldAttestationVerifierAddress, + ); + await tx.wait(); + console.log( + "Successfully set DkgFoldAttestationVerifier on CiphernodeRegistry", + ); + } + } + const verifierLines = verifierEntries.length > 0 ? verifierEntries.map(([name, addr]) => ` ${name}: ${addr}`).join("\n") @@ -468,6 +503,7 @@ export const deployEnclave = async ( PkVerifier (BFV): ${pkVerifierAddress} Circuit Verifiers: ${verifierLines} + DkgFoldAttestationVerifier: ${dkgFoldAttestationVerifierAddress ?? "(not deployed)"} ============================================ `); }; diff --git a/packages/enclave-contracts/scripts/runConfigureSlashingPolicies.ts b/packages/enclave-contracts/scripts/runConfigureSlashingPolicies.ts new file mode 100644 index 0000000000..228c7ad27c --- /dev/null +++ b/packages/enclave-contracts/scripts/runConfigureSlashingPolicies.ts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import hre from "hardhat"; + +import { configureLocalSlashingPolicies } from "./configureLocalSlashingPolicies"; + +configureLocalSlashingPolicies(hre).catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/packages/enclave-contracts/scripts/runVerification.ts b/packages/enclave-contracts/scripts/runVerification.ts index f1b7b29988..95036d0a73 100644 --- a/packages/enclave-contracts/scripts/runVerification.ts +++ b/packages/enclave-contracts/scripts/runVerification.ts @@ -5,12 +5,11 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import hre from "hardhat"; +import { getDeploymentChain } from "./utils"; import { verifyContracts } from "./verify"; async function main() { - const { ethers } = await hre.network.connect(); - const [signer] = await ethers.getSigners(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); verifyContracts(chain); } diff --git a/packages/enclave-contracts/scripts/syncIntegrationConfig.ts b/packages/enclave-contracts/scripts/syncIntegrationConfig.ts new file mode 100644 index 0000000000..6968ae4149 --- /dev/null +++ b/packages/enclave-contracts/scripts/syncIntegrationConfig.ts @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import path from "path"; +import { fileURLToPath } from "url"; + +import { updateE3Config } from "./utils"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const contractMapping: Record = { + MockE3Program: "e3_program", + Enclave: "enclave", + CiphernodeRegistryOwnable: "ciphernode_registry", + BondingRegistry: "bonding_registry", + SlashingManager: "slashing_manager", + MockUSDC: "fee_token", +}; + +const integrationConfigPath = path.resolve( + __dirname, + "../../../tests/integration/enclave.config.yaml", +); + +const chain = process.env.HARDHAT_NETWORK ?? "localhost"; + +updateE3Config(chain, integrationConfigPath, contractMapping); diff --git a/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts b/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts index de36fc2ff7..cf14456bdb 100644 --- a/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts +++ b/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts @@ -6,7 +6,7 @@ import hre from "hardhat"; import { upgradeAndSaveBondingRegistry } from "../deployAndSave/bondingRegistry"; -import { readDeploymentArgs } from "../utils"; +import { getDeploymentChain, readDeploymentArgs } from "../utils"; /** * Upgrades the BondingRegistry contract implementation and saves the deployment arguments @@ -16,7 +16,7 @@ export const upgradeBondingRegistry = async () => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); const signerAddress = await signer.getAddress(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); console.log("Signer:", signerAddress); const preDeployedArgs = readDeploymentArgs("BondingRegistry", chain); diff --git a/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts b/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts index 32e095bb41..4b767cf1a6 100644 --- a/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts +++ b/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts @@ -7,7 +7,7 @@ import hre from "hardhat"; import { upgradeAndSaveCiphernodeRegistryOwnable } from "../deployAndSave/ciphernodeRegistryOwnable"; import { deployAndSavePoseidonT3 } from "../deployAndSave/poseidonT3"; -import { readDeploymentArgs } from "../utils"; +import { getDeploymentChain, readDeploymentArgs } from "../utils"; /** * Upgrades the CiphernodeRegistryOwnable contract implementation and saves the deployment arguments @@ -17,7 +17,7 @@ export const upgradeCiphernodeRegistryOwnable = async () => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); const signerAddress = await signer.getAddress(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); console.log("Signer:", signerAddress); const poseidonT3 = await deployAndSavePoseidonT3({ hre }); diff --git a/packages/enclave-contracts/scripts/upgrade/enclave.ts b/packages/enclave-contracts/scripts/upgrade/enclave.ts index 0e5fa3e2a0..8ed56ef2cd 100644 --- a/packages/enclave-contracts/scripts/upgrade/enclave.ts +++ b/packages/enclave-contracts/scripts/upgrade/enclave.ts @@ -6,7 +6,7 @@ import hre from "hardhat"; import { upgradeAndSaveEnclave } from "../deployAndSave/enclave"; -import { readDeploymentArgs } from "../utils"; +import { getDeploymentChain, readDeploymentArgs } from "../utils"; /** * Upgrades the Enclave contract implementation and saves the deployment arguments @@ -16,7 +16,7 @@ export const upgradeEnclave = async () => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); const signerAddress = await signer.getAddress(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); console.log("Signer:", signerAddress); const preDeployedArgs = readDeploymentArgs("Enclave", chain); diff --git a/packages/enclave-contracts/scripts/utils.ts b/packages/enclave-contracts/scripts/utils.ts index 33c05e8c85..beb8d12303 100644 --- a/packages/enclave-contracts/scripts/utils.ts +++ b/packages/enclave-contracts/scripts/utils.ts @@ -5,6 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import { getBytes, hexlify, zeroPadValue } from "ethers"; import fs from "fs"; +import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; import { fileURLToPath } from "node:url"; import path from "path"; @@ -23,11 +24,60 @@ export function committeeHashFromLimbs(hi: string, lo: string): string { export const deploymentsFile = path.join("deployed_contracts.json"); -/** Monorepo root (`enclave/`), resolved from `packages/enclave-contracts/scripts/`. */ -export const REPO_ROOT = path.resolve( - path.dirname(fileURLToPath(import.meta.url)), - "../../..", -); +/** Hardhat network names used for local development. */ +export const LOCAL_DEPLOYMENT_NETWORKS = [ + "localhost", + "hardhat", + "anvil", + "ganache", +] as const; + +/** + * Legacy deployment bucket keys written when scripts used `provider.getNetwork().name` + * (ethers reports "default" / "undefined" on local chains). Cleared with local deploys. + */ +export const LEGACY_LOCAL_DEPLOYMENT_ALIASES = [ + "default", + "undefined", +] as const; + +/** + * Chain key for `deployed_contracts.json`. Use Hardhat's network name, not the provider's + * `network.name` (which is often `"default"` on localhost and does not match clean/deploy). + */ +export const getDeploymentChain = (hre: HardhatRuntimeEnvironment): string => + hre.globalOptions.network ?? "localhost"; + +export const isLocalDeploymentChain = (chain: string): boolean => + (LOCAL_DEPLOYMENT_NETWORKS as readonly string[]).includes(chain) || + (LEGACY_LOCAL_DEPLOYMENT_ALIASES as readonly string[]).includes(chain); + +/** Monorepo root (`enclave/`). Works from `scripts/` and compiled `dist/scripts/`. */ +function resolveRepoRoot(): string { + let dir = path.dirname(fileURLToPath(import.meta.url)); + const root = path.parse(dir).root; + while (dir !== root) { + const pkgPath = path.join(dir, "package.json"); + if (fs.existsSync(pkgPath)) { + try { + const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")) as { + name?: string; + }; + if (pkg.name === "@enclave/main") { + return dir; + } + } catch { + // keep walking + } + } + dir = path.dirname(dir); + } + throw new Error( + "Could not find enclave repo root (expected package.json name @enclave/main)", + ); +} + +export const REPO_ROOT = resolveRepoRoot(); /** * Default insecure-512 / micro committee layout for BFV aggregator verifiers. @@ -90,7 +140,7 @@ export const BFV_DECRYPTION_SUB_CIRCUIT_VK_HASH_PATHS = { export function readVkRecursiveHash(filePath: string): string { if (!fs.existsSync(filePath)) { throw new Error( - `Missing circuit VK hash file: ${filePath}. Run pnpm compile:circuits.`, + `Missing circuit VK hash file: ${filePath}. From repo root run: pnpm build:circuits --preset insecure-512`, ); } @@ -309,6 +359,41 @@ export const cleanDeployments = (network: string): void => { fs.writeFileSync(deploymentsFile, JSON.stringify(deployments, null, 2)); }; +/** + * Remove deployment records for a local Hardhat network and legacy provider-name buckets. + */ +export const cleanLocalDeployments = (network: string): void => { + const isLocalNetwork = + (LOCAL_DEPLOYMENT_NETWORKS as readonly string[]).includes(network) || + (LEGACY_LOCAL_DEPLOYMENT_ALIASES as readonly string[]).includes(network); + + const targets = new Set([network]); + if (isLocalNetwork) { + for (const name of LOCAL_DEPLOYMENT_NETWORKS) { + targets.add(name); + } + for (const alias of LEGACY_LOCAL_DEPLOYMENT_ALIASES) { + targets.add(alias); + } + } + + if (!fs.existsSync(deploymentsFile)) { + return; + } + + const deployments = readAllDeployments(); + let changed = false; + for (const key of targets) { + if (deployments[key]) { + delete deployments[key]; + changed = true; + } + } + if (changed) { + fs.writeFileSync(deploymentsFile, JSON.stringify(deployments, null, 2)); + } +}; + /** * Check if two arrays are equal by checking the values inside * @param arr1 - The first array diff --git a/packages/enclave-contracts/tasks/ciphernode.ts b/packages/enclave-contracts/tasks/ciphernode.ts index 59d1b99bc4..c013ef97a8 100644 --- a/packages/enclave-contracts/tasks/ciphernode.ts +++ b/packages/enclave-contracts/tasks/ciphernode.ts @@ -7,6 +7,45 @@ import { ZeroAddress } from "ethers"; import { task } from "hardhat/config"; import { ArgumentType } from "hardhat/types/arguments"; +/** + * Resolve the right impersonation RPC prefix for the connected provider. + * Anvil exposes `anvil_*`; Hardhat's in-process network exposes `hardhat_*`. + * Probes once via `anvil_impersonateAccount` and falls back to `hardhat_*`. + * Throws a clear error if neither is supported (e.g. a real RPC). + */ +async function resolveImpersonationRpc( + provider: { send: (method: string, params: unknown[]) => Promise }, + probeAddress: string, +): Promise<{ + impersonate: string; + setBalance: string; + stopImpersonating: string; +}> { + try { + await provider.send("anvil_impersonateAccount", [probeAddress]); + await provider.send("anvil_stopImpersonatingAccount", [probeAddress]); + return { + impersonate: "anvil_impersonateAccount", + setBalance: "anvil_setBalance", + stopImpersonating: "anvil_stopImpersonatingAccount", + }; + } catch { + try { + await provider.send("hardhat_impersonateAccount", [probeAddress]); + await provider.send("hardhat_stopImpersonatingAccount", [probeAddress]); + return { + impersonate: "hardhat_impersonateAccount", + setBalance: "hardhat_setBalance", + stopImpersonating: "hardhat_stopImpersonatingAccount", + }; + } catch { + throw new Error( + "Provider does not support account impersonation. Run this task against an Anvil or Hardhat local node (e.g. `--network localhost` with `anvil` or `npx hardhat node`)", + ); + } + } +} + export const ciphernodeAdd = task( "ciphernode:add", "Register a ciphernode to the bonding registry and ciphernode registry", @@ -280,7 +319,7 @@ export const ciphernodeMintTokens = task( export const ciphernodeAdminAdd = task( "ciphernode:admin-add", - "Register a ciphernode using admin privileges (for testing/setup)", + "Register a ciphernode using admin privileges (for testing/setup). Requires a local node that supports account impersonation (Anvil or Hardhat).", ) .addOption({ name: "ciphernodeAddress", @@ -312,6 +351,13 @@ export const ciphernodeAdminAdd = task( ) => { const connection = await hre.network.connect(); const { ethers } = connection; + const provider = ethers.provider; + + if (!provider) { + throw new Error( + "No provider on Hardhat network connection. Use --network localhost with Anvil running.", + ); + } if (ciphernodeAddress === ZeroAddress) { throw new Error( @@ -319,14 +365,10 @@ export const ciphernodeAdminAdd = task( ); } - let adminWallet; - if (adminPrivateKey) { - adminWallet = new ethers.Wallet(adminPrivateKey, ethers.provider); - } else { - const anvilFirstKey = - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; - adminWallet = new ethers.Wallet(anvilFirstKey, ethers.provider); - } + const [defaultSigner] = await ethers.getSigners(); + const adminWallet = adminPrivateKey + ? new ethers.Wallet(adminPrivateKey, provider) + : defaultSigner; console.log(`Admin wallet: ${adminWallet.address}`); console.log(`Registering ciphernode: ${ciphernodeAddress}`); @@ -334,24 +376,38 @@ export const ciphernodeAdminAdd = task( const { deployAndSaveBondingRegistry } = await import( "../scripts/deployAndSave/bondingRegistry" ); - const { bondingRegistry } = await deployAndSaveBondingRegistry({ hre }); - + const { deployAndSaveEnclaveTicketToken } = await import( + "../scripts/deployAndSave/enclaveTicketToken" + ); const { deployAndSaveEnclaveToken } = await import( "../scripts/deployAndSave/enclaveToken" ); - const { enclaveToken } = await deployAndSaveEnclaveToken({ hre }); - const { deployAndSaveMockStableToken } = await import( "../scripts/deployAndSave/mockStableToken" ); + const { bondingRegistry } = await deployAndSaveBondingRegistry({ hre }); + const { enclaveToken } = await deployAndSaveEnclaveToken({ hre }); + const { enclaveTicketToken } = await deployAndSaveEnclaveTicketToken({ + hre, + }); const { mockStableToken: mockUSDC } = await deployAndSaveMockStableToken({ hre, + initialSupply: 1_000_000, }); + const bondingRegistryAddress = await bondingRegistry.getAddress(); + const registryCode = await provider.getCode(bondingRegistryAddress); + if (registryCode === "0x") { + throw new Error( + `BondingRegistry not deployed at ${bondingRegistryAddress}. ` + + "Run pnpm evm:deploy with Anvil on localhost:8545 first.", + ); + } + const enclaveTokenConnected = enclaveToken.connect(adminWallet); const mockUSDCConnected = mockUSDC.connect(adminWallet); - const ticketTokenAddress = await bondingRegistry.ticketToken(); + const ticketTokenAddress = await enclaveTicketToken.getAddress(); try { const licenseBondWei = ethers.parseEther(licenseBondAmount); @@ -384,15 +440,15 @@ export const ciphernodeAdminAdd = task( console.log( "Step 3: Impersonating ciphernode for license operations...", ); - await connection.provider.request({ - method: "hardhat_impersonateAccount", - params: [ciphernodeAddress], - }); - - await connection.provider.request({ - method: "hardhat_setBalance", - params: [ciphernodeAddress, "0x1000000000000000000000"], - }); + const impersonationRpc = await resolveImpersonationRpc( + provider, + ciphernodeAddress, + ); + await provider.send(impersonationRpc.impersonate, [ciphernodeAddress]); + await provider.send(impersonationRpc.setBalance, [ + ciphernodeAddress, + "0x1000000000000000000000", + ]); const ciphernodeSigner = await ethers.getSigner(ciphernodeAddress); const enclaveTokenAsCiphernode = enclaveToken.connect(ciphernodeSigner); @@ -416,10 +472,9 @@ export const ciphernodeAdminAdd = task( "Operator registered (automatically added to CiphernodeRegistry)", ); - await connection.provider.request({ - method: "hardhat_stopImpersonatingAccount", - params: [ciphernodeAddress], - }); + await provider.send(impersonationRpc.stopImpersonating, [ + ciphernodeAddress, + ]); console.log("Step 4: Adding ticket balance via admin..."); @@ -429,15 +484,11 @@ export const ciphernodeAdminAdd = task( ); await approveUsdcTx.wait(); - await connection.provider.request({ - method: "hardhat_impersonateAccount", - params: [ciphernodeAddress], - }); - - await connection.provider.request({ - method: "hardhat_setBalance", - params: [ciphernodeAddress, "0x1000000000000000000000"], - }); + await provider.send(impersonationRpc.impersonate, [ciphernodeAddress]); + await provider.send(impersonationRpc.setBalance, [ + ciphernodeAddress, + "0x1000000000000000000000", + ]); const ciphernodeSigner2 = await ethers.getSigner(ciphernodeAddress); const bondingRegistryAsCiphernode2 = @@ -461,10 +512,9 @@ export const ciphernodeAdminAdd = task( await addTicketTx.wait(); console.log(`Ticket balance added: ${ticketAmount} USDC worth`); - await connection.provider.request({ - method: "hardhat_stopImpersonatingAccount", - params: [ciphernodeAddress], - }); + await provider.send(impersonationRpc.stopImpersonating, [ + ciphernodeAddress, + ]); const isRegistered = await bondingRegistry.isRegistered(ciphernodeAddress); diff --git a/packages/enclave-contracts/tasks/enclave.ts b/packages/enclave-contracts/tasks/enclave.ts index bb69284e90..ad2716520e 100644 --- a/packages/enclave-contracts/tasks/enclave.ts +++ b/packages/enclave-contracts/tasks/enclave.ts @@ -385,6 +385,7 @@ export const publishCommittee = task( publicKey, pkCommitment, proof, + "0x", ); console.log("Publishing committee... ", tx.hash); diff --git a/packages/enclave-contracts/tasks/program.ts b/packages/enclave-contracts/tasks/program.ts index b9104227ce..056355e76b 100644 --- a/packages/enclave-contracts/tasks/program.ts +++ b/packages/enclave-contracts/tasks/program.ts @@ -7,6 +7,8 @@ import fs from "fs"; import { task } from "hardhat/config"; import { ArgumentType } from "hardhat/types/arguments"; +import { readDeploymentArgs } from "../scripts/utils"; + export const publishInput = task( "e3-program:publishInput", "Publish input for an E3 program", @@ -29,11 +31,13 @@ export const publishInput = task( defaultValue: "", type: ArgumentType.STRING, }) - // MockProgram + // MockProgram. Defaults to the address in deployed_contracts.json for the + // active network; pass --program-address to override. .addOption({ name: "programAddress", - description: "Address of the E3 program", - defaultValue: "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F", + description: + "Address of the E3 program (defaults to deployed MockE3Program)", + defaultValue: "", type: ArgumentType.STRING, }) .setAction(async () => ({ @@ -47,10 +51,18 @@ export const publishInput = task( const [signer] = await ethers.getSigners(); let actualProgramAddress = programAddress; - if (programAddress === "") { - actualProgramAddress = await deployAndSaveMockProgram({ hre }).then( - ({ e3Program }) => e3Program.getAddress(), + if (!actualProgramAddress) { + const deployed = readDeploymentArgs( + "MockE3Program", + hre.globalOptions.network, ); + if (deployed?.address) { + actualProgramAddress = deployed.address; + } else { + actualProgramAddress = await deployAndSaveMockProgram({ hre }).then( + ({ e3Program }) => e3Program.getAddress(), + ); + } } const program = MockE3Program__factory.connect( @@ -62,12 +74,59 @@ export const publishInput = task( if (dataFile) { const file = fs.readFileSync(dataFile); - dataToSend = file.toString(); + // Hex-encode binary file contents so ethers ABI-encodes them as `bytes`. + dataToSend = "0x" + file.toString("hex"); } await program.publishInput(e3Id, dataToSend); - console.log(`Input published`); + console.log(`Input published to ${actualProgramAddress} (e3Id=${e3Id})`); + }, + })) + .build(); + +// Wire MockE3Program → Enclave so `publishInput` forwards to +// `publishCiphertextOutput`. Off by default; the proof-aggregation integration +// flow opts in by calling this once after deploy. The non-aggregation `base` +// flow does NOT wire it, preserving the pre-existing fake_encrypt path which +// posts the ciphertext via `e3:publishCiphertext` directly. +export const setMockProgramEnclave = task( + "e3-program:setMockEnclave", + "Wire MockE3Program → Enclave for the proof-aggregation integration test", +) + .setAction(async () => ({ + default: async (_args, hre) => { + const { ethers } = await hre.network.connect(); + const [signer] = await ethers.getSigners(); + const network = hre.globalOptions.network; + + const mockArgs = readDeploymentArgs("MockE3Program", network); + const enclaveArgs = readDeploymentArgs("Enclave", network); + if (!mockArgs?.address || !enclaveArgs?.address) { + throw new Error( + "MockE3Program or Enclave deployment not found; deploy first.", + ); + } + + // Use ABI fragments directly so this works even when typechain types + // haven't been regenerated. + const mockProgram = new ethers.Contract( + mockArgs.address, + [ + "function enclave() view returns (address)", + "function setEnclave(address) external", + ], + signer, + ); + const current: string = await mockProgram.enclave(); + if (current.toLowerCase() === enclaveArgs.address.toLowerCase()) { + console.log(`MockE3Program already wired to ${enclaveArgs.address}`); + return; + } + await mockProgram.setEnclave(enclaveArgs.address); + console.log( + `MockE3Program ${mockArgs.address} → Enclave ${enclaveArgs.address}`, + ); }, })) .build(); diff --git a/packages/enclave-contracts/tasks/utils.ts b/packages/enclave-contracts/tasks/utils.ts index 040cda2cf2..ace9e09eaf 100644 --- a/packages/enclave-contracts/tasks/utils.ts +++ b/packages/enclave-contracts/tasks/utils.ts @@ -6,7 +6,7 @@ import { task } from "hardhat/config"; import { ArgumentType } from "hardhat/types/arguments"; -import { cleanDeployments } from "../scripts/utils"; +import { cleanLocalDeployments } from "../scripts/utils"; export const cleanDeploymentsTask = task( "utils:clean-deployments", @@ -20,7 +20,7 @@ export const cleanDeploymentsTask = task( }) .setAction(async () => ({ default: ({ chain }) => { - cleanDeployments(chain); + cleanLocalDeployments(chain); }, })) .build(); diff --git a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts index de60bab583..5395c9c291 100644 --- a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts +++ b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts @@ -141,7 +141,7 @@ describe("BfvVkBindingIntegration", function () { const zkTranscriptLibAddress = await zkTranscriptLib.getAddress(); const dkgAggFactory = await ethers.getContractFactory( - "DkgAggregatorVerifier", + "contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:DkgAggregatorVerifier", { libraries: { "project/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:ZKTranscriptLib": @@ -153,7 +153,7 @@ describe("BfvVkBindingIntegration", function () { await dkgAgg.waitForDeployment(); const decAggFactory = await ethers.getContractFactory( - "DecryptionAggregatorVerifier", + "contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol:DecryptionAggregatorVerifier", { libraries: { "project/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol:ZKTranscriptLib": @@ -380,12 +380,15 @@ describe("BfvVkBindingIntegration", function () { await zkTranscriptLib.waitForDeployment(); const dkgAgg = await ( - await ethers.getContractFactory("DkgAggregatorVerifier", { - libraries: { - "project/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:ZKTranscriptLib": - await zkTranscriptLib.getAddress(), + await ethers.getContractFactory( + "contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:DkgAggregatorVerifier", + { + libraries: { + "project/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:ZKTranscriptLib": + await zkTranscriptLib.getAddress(), + }, }, - }) + ) ).deploy(); await dkgAgg.waitForDeployment(); diff --git a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts index c1ac66d8f1..476afa0f89 100644 --- a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts +++ b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts @@ -257,7 +257,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); // Verify stage transitioned to KeyPublished (after publishCommittee which calls onKeyPublished) stage = await enclave.getE3Stage(0); @@ -297,7 +297,9 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await expect(registry.publishCommittee(0, publicKey, pkCommitment, "0x")) + await expect( + registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"), + ) .to.emit(enclave, "CommitteeFormed") .withArgs(0); }); @@ -555,7 +557,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); // 2. Wait past compute deadline → mark as failed const e3 = await enclave.getE3(0); @@ -655,7 +657,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); // 2. Fail via compute timeout const e3 = await enclave.getE3(0); @@ -899,7 +901,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); stage = await enclave.getE3Stage(0); expect(stage).to.equal(3); // KeyPublished @@ -973,7 +975,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); stage = await enclave.getE3Stage(0); expect(stage).to.equal(3); // KeyPublished @@ -1211,7 +1213,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); expect(await enclave.getE3Stage(0)).to.equal(3); // KeyPublished @@ -1365,7 +1367,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); expect(await enclave.getE3Stage(0)).to.equal(3); // KeyPublished @@ -1419,7 +1421,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); // Publish outputs const e3 = await enclave.getE3(0); diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index c870e0b0f3..c5d43c7596 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -7,15 +7,12 @@ import { expect } from "chai"; import { ADDRESS_TWO as AddressTwo, - DATA as data, + buildMockAggregationPublishArgs, deployEnclaveSystem, - encodeMockDkgProof, - BFV_PARAMS_DEFAULT as encodedE3ProgramParams, ENCRYPTION_SCHEME_ID as encryptionSchemeId, ethers, makeRequest, networkHelpers, - PROOF as proof, setupAndPublishCommittee, DEFAULT_TIMEOUT_CONFIG as timeoutConfig, } from "./fixtures"; @@ -27,10 +24,32 @@ describe("Enclave", function () { "0x0000000000000000000000000000000000000000000000000000000000000002"; const abiCoder = ethers.AbiCoder.defaultAbiCoder(); + + const polynomial_degree = ethers.toBigInt(512); + const plaintext_modulus = ethers.toBigInt(10); + const moduli = [ + ethers.toBigInt("0xffffee001"), + ethers.toBigInt("0xffffc4001"), + ]; + + const encodedE3ProgramParams = abiCoder.encode( + ["uint256", "uint256", "uint256[]"], + [polynomial_degree, plaintext_modulus, moduli], + ); + + const data = "0xda7a"; + const proof = "0x1337"; + const inputWindowDuration = 300; const setup = async () => { const sys = await deployEnclaveSystem({ wireSlashingManager: false }); + const dkgFoldAttestationVerifier = await ethers.deployContract( + "DkgFoldAttestationVerifier", + ); + await sys.ciphernodeRegistry.setInitialDkgFoldAttestationVerifier( + await dkgFoldAttestationVerifier.getAddress(), + ); return { owner: sys.owner, notTheOwner: sys.notTheOwner, @@ -44,6 +63,7 @@ describe("Enclave", function () { ticketToken: sys.ticketToken, usdcToken: sys.usdcToken, slashingManager: sys.slashingManager, + dkgFoldAttestationVerifier, request: sys.request, mocks: { decryptionVerifier: sys.mocks.decryptionVerifier, @@ -787,6 +807,7 @@ describe("Enclave", function () { operator1, operator2, operator3, + dkgFoldAttestationVerifier, } = await loadFixture(setup); const e3Id = 0; @@ -796,13 +817,20 @@ describe("Enclave", function () { proofAggregationEnabled: true, }); - const pkCommitment = ethers.keccak256(data); + const operators = [operator1, operator2, operator3]; + const { proof, bundle } = await buildMockAggregationPublishArgs( + operators, + e3Id, + data, + await dkgFoldAttestationVerifier.getAddress(), + ); await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, data, - [operator1, operator2, operator3], - encodeMockDkgProof(pkCommitment), + operators, + proof, + bundle, ); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); diff --git a/packages/enclave-contracts/test/Pricing/DustRotation.spec.ts b/packages/enclave-contracts/test/Pricing/DustRotation.spec.ts index 53d258535e..8857bf7897 100644 --- a/packages/enclave-contracts/test/Pricing/DustRotation.spec.ts +++ b/packages/enclave-contracts/test/Pricing/DustRotation.spec.ts @@ -37,7 +37,7 @@ describe("Pricing — per-E3 dust rotation across consecutive E3s", function () await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(e3Id); const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x", "0x"); }; const setup = async () => { @@ -128,10 +128,11 @@ describe("Pricing — per-E3 dust rotation across consecutive E3s", function () }; await feeToken.approve(await enclave.getAddress(), ethers.MaxUint256); await enclave.request(req); + // topNodes are sorted by ascending address; operator3 < operator1 < operator2 const nodes = [ + await operator3.getAddress(), await operator1.getAddress(), await operator2.getAddress(), - await operator3.getAddress(), ]; await setupAndPublishCommittee( ciphernodeRegistryContract, diff --git a/packages/enclave-contracts/test/Pricing/PullPaymentsAndAllowlist.spec.ts b/packages/enclave-contracts/test/Pricing/PullPaymentsAndAllowlist.spec.ts index ceb860677a..ee7001c80b 100644 --- a/packages/enclave-contracts/test/Pricing/PullPaymentsAndAllowlist.spec.ts +++ b/packages/enclave-contracts/test/Pricing/PullPaymentsAndAllowlist.spec.ts @@ -32,7 +32,7 @@ describe("Enclave — pull payments + fee-token allow-list", function () { await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(e3Id); const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x", "0x"); }; // Two fixtures: one using vanilla USDC (allow-list tests), @@ -182,11 +182,6 @@ describe("Enclave — pull payments + fee-token allow-list", function () { }; await enclave.request(req2); const e3Id2 = 1; - const nodes = [ - await ctx.operator1.getAddress(), - await ctx.operator2.getAddress(), - await ctx.operator3.getAddress(), - ]; await setupAndPublishCommittee( ctx.ciphernodeRegistryContract, e3Id2, diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts index b838daf0c4..201f754a3c 100644 --- a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts @@ -235,15 +235,17 @@ describe("CiphernodeRegistryOwnable", function () { await finalizeCommitteeAfterWindow(registry, 0); await expect( - registry.connect(notTheOwner).publishCommittee(0, data, dataHash, "0x"), + registry + .connect(notTheOwner) + .publishCommittee(0, data, dataHash, "0x", "0x"), ) .to.emit(registry, "CommitteePublished") .withArgs( 0, [ + await operator3.getAddress(), await operator1.getAddress(), await operator2.getAddress(), - await operator3.getAddress(), ], data, dataHash, @@ -273,7 +275,7 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator3).submitTicket(0, 1); await finalizeCommitteeAfterWindow(registry, 0); - await registry.publishCommittee(0, data, dataHash, "0x"); + await registry.publishCommittee(0, data, dataHash, "0x", "0x"); expect(await registry.committeePublicKey(0)).to.equal(dataHash); }); it("emits a CommitteePublished event", async function () { @@ -300,14 +302,16 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator3).submitTicket(0, 1); await finalizeCommitteeAfterWindow(registry, 0); - await expect(await registry.publishCommittee(0, data, dataHash, "0x")) + await expect( + await registry.publishCommittee(0, data, dataHash, "0x", "0x"), + ) .to.emit(registry, "CommitteePublished") .withArgs( 0, [ + await operator3.getAddress(), await operator1.getAddress(), await operator2.getAddress(), - await operator3.getAddress(), ], data, dataHash, @@ -471,7 +475,7 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator3).submitTicket(e3Id, 1); await finalizeCommitteeAfterWindow(registry, e3Id); - await registry.publishCommittee(e3Id, data, dataHash, "0x"); + await registry.publishCommittee(e3Id, data, dataHash, "0x", "0x"); expect(await registry.committeePublicKey(e3Id)).to.equal(dataHash); }); it("reverts if the committee has not been published", async function () { diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryVerifierLifecycle.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryVerifierLifecycle.spec.ts new file mode 100644 index 0000000000..7cbbfc1e05 --- /dev/null +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryVerifierLifecycle.spec.ts @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import { expect } from "chai"; + +import { deployEnclaveSystem, ethers, networkHelpers } from "../fixtures"; + +const { loadFixture, time } = networkHelpers; + +describe("CiphernodeRegistryOwnable verifier lifecycle", function () { + const setup = async () => { + const sys = await deployEnclaveSystem(); + const verifier1 = await ethers.deployContract("DkgFoldAttestationVerifier"); + const verifier2 = await ethers.deployContract("DkgFoldAttestationVerifier"); + const verifier3 = await ethers.deployContract("DkgFoldAttestationVerifier"); + return { + owner: sys.owner, + notTheOwner: sys.notTheOwner, + registry: sys.ciphernodeRegistry, + verifier1, + verifier2, + verifier3, + }; + }; + + it("supports propose/commit with timelock enforcement", async function () { + const { registry, verifier1, verifier2 } = await loadFixture(setup); + + await registry.setInitialDkgFoldAttestationVerifier( + await verifier1.getAddress(), + ); + + const timelock = await registry.DKG_FOLD_VERIFIER_TIMELOCK(); + + await expect( + registry.proposeDkgFoldAttestationVerifier(await verifier2.getAddress()), + ) + .to.emit(registry, "DkgFoldAttestationVerifierProposed") + .withArgs( + await verifier2.getAddress(), + (readyAt: bigint) => readyAt > 0n, + ); + + await expect( + registry.commitDkgFoldAttestationVerifier(await verifier2.getAddress()), + ).to.be.revertedWithCustomError(registry, "VerifierUpdateTimelockActive"); + + await time.increase(Number(timelock) + 1); + + await expect( + registry.commitDkgFoldAttestationVerifier(await verifier2.getAddress()), + ) + .to.emit(registry, "DkgFoldAttestationVerifierUpdated") + .withArgs(await verifier2.getAddress()); + + expect(await registry.dkgFoldAttestationVerifier()).to.equal( + await verifier2.getAddress(), + ); + }); + + it("rejects commit with verifier mismatch", async function () { + const { registry, verifier1, verifier2, verifier3 } = + await loadFixture(setup); + + await registry.setInitialDkgFoldAttestationVerifier( + await verifier1.getAddress(), + ); + await registry.proposeDkgFoldAttestationVerifier( + await verifier2.getAddress(), + ); + await time.increase( + Number(await registry.DKG_FOLD_VERIFIER_TIMELOCK()) + 1, + ); + + await expect( + registry.commitDkgFoldAttestationVerifier(await verifier3.getAddress()), + ) + .to.be.revertedWithCustomError(registry, "VerifierMismatch") + .withArgs(await verifier2.getAddress(), await verifier3.getAddress()); + }); + + it("cleans up stale pending proposal on setInitial", async function () { + const { registry, verifier1, verifier2 } = await loadFixture(setup); + + await registry.proposeDkgFoldAttestationVerifier( + await verifier2.getAddress(), + ); + + await expect( + registry.setInitialDkgFoldAttestationVerifier( + await verifier1.getAddress(), + ), + ) + .to.emit(registry, "DkgFoldAttestationVerifierProposalCancelled") + .withArgs(await verifier2.getAddress()) + .and.to.emit(registry, "DkgFoldAttestationVerifierUpdated") + .withArgs(await verifier1.getAddress()); + + expect(await registry.pendingDkgFoldAttestationVerifier()).to.equal( + ethers.ZeroAddress, + ); + expect(await registry.pendingDkgFoldAttestationVerifierAt()).to.equal(0); + + await expect( + registry.commitDkgFoldAttestationVerifier(await verifier2.getAddress()), + ).to.be.revertedWithCustomError(registry, "NoPendingVerifierUpdate"); + }); + + it("cancels a pending proposal", async function () { + const { registry, verifier2 } = await loadFixture(setup); + + await registry.proposeDkgFoldAttestationVerifier( + await verifier2.getAddress(), + ); + + await expect(registry.cancelDkgFoldAttestationVerifierProposal()) + .to.emit(registry, "DkgFoldAttestationVerifierProposalCancelled") + .withArgs(await verifier2.getAddress()); + + expect(await registry.pendingDkgFoldAttestationVerifier()).to.equal( + ethers.ZeroAddress, + ); + expect(await registry.pendingDkgFoldAttestationVerifierAt()).to.equal(0); + }); + + it("requires timelock to set accusationVoteValidity to zero", async function () { + const { registry } = await loadFixture(setup); + + await expect( + registry.setAccusationVoteValidity(0), + ).to.be.revertedWithCustomError( + registry, + "AccusationVoteValidityZeroRequiresTimelock", + ); + + await registry.proposeAccusationVoteValidity(0); + await expect( + registry.commitAccusationVoteValidity(0), + ).to.be.revertedWithCustomError( + registry, + "AccusationVoteValidityTimelockActive", + ); + + await time.increase( + Number(await registry.ACCUSATION_VOTE_VALIDITY_TIMELOCK()) + 1, + ); + + await expect(registry.commitAccusationVoteValidity(0)) + .to.emit(registry, "AccusationVoteValiditySet") + .withArgs(0); + expect(await registry.accusationVoteValidity()).to.equal(0); + }); + + it("cancels pending accusationVoteValidity proposal", async function () { + const { registry } = await loadFixture(setup); + + await registry.proposeAccusationVoteValidity(1234); + await expect(registry.cancelAccusationVoteValidityProposal()) + .to.emit(registry, "AccusationVoteValidityProposalCancelled") + .withArgs(1234); + + expect(await registry.pendingAccusationVoteValidity()).to.equal(0); + expect(await registry.pendingAccusationVoteValidityAt()).to.equal(0); + }); +}); diff --git a/packages/enclave-contracts/test/Registry/DkgFoldAttestationVerifier.spec.ts b/packages/enclave-contracts/test/Registry/DkgFoldAttestationVerifier.spec.ts new file mode 100644 index 0000000000..7bddfc7543 --- /dev/null +++ b/packages/enclave-contracts/test/Registry/DkgFoldAttestationVerifier.spec.ts @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import { expect } from "chai"; + +import { + buildMockDkgAttestationFixtureData, + deployEnclaveSystem, + ethers, + networkHelpers, +} from "../fixtures"; + +const { loadFixture } = networkHelpers; + +describe("DkgFoldAttestationVerifier", function () { + const e3Id = 7; + + const setup = async () => { + const sys = await deployEnclaveSystem({ + useMockCiphernodeRegistry: true, + setupOperators: 0, + wireSlashingManager: false, + }); + const verifier = await ethers.deployContract("DkgFoldAttestationVerifier"); + const signers = await ethers.getSigners(); + const operators = [signers[2], signers[3], signers[4]]; + return { + owner: sys.owner, + mockRegistry: sys.mockCiphernodeRegistry!, + verifier, + operators, + }; + }; + + it("verifies a valid bundle and returns anchors", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const { ordered, proof, bundle, skCommits, esmCommits } = + await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + await verifier.getAddress(), + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + const [partyIds, skAggCommits, esmAggCommits] = await verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + proof, + bundle, + ); + + expect(partyIds.map((v: bigint) => Number(v))).to.deep.equal([0, 1, 2]); + expect(skAggCommits).to.deep.equal(skCommits); + expect(esmAggCommits).to.deep.equal(esmCommits); + }); + + it("reverts on out-of-order attestations", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const { ordered, proof, attestations, bindings } = + await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + await verifier.getAddress(), + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + const badAttestations = [...attestations]; + [badAttestations[0], badAttestations[1]] = [ + badAttestations[1], + badAttestations[0], + ]; + const badBundle = ethers.AbiCoder.defaultAbiCoder().encode( + [ + "tuple(uint256 partyId, bytes32 skAggCommit, bytes32 esmAggCommit, bytes signature)[]", + "tuple(uint256 partyId, address node)[]", + ], + [badAttestations, bindings], + ); + + await expect( + verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + proof, + badBundle, + ), + ).to.be.revertedWithCustomError(mockRegistry, "InvalidFoldAttestation"); + }); + + it("reverts on duplicate binding partyId", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const { ordered, proof, attestations, bindings } = + await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + await verifier.getAddress(), + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + const badBindings = [...bindings]; + badBindings[1] = { ...badBindings[1], partyId: badBindings[0].partyId }; + const badBundle = ethers.AbiCoder.defaultAbiCoder().encode( + [ + "tuple(uint256 partyId, bytes32 skAggCommit, bytes32 esmAggCommit, bytes signature)[]", + "tuple(uint256 partyId, address node)[]", + ], + [attestations, badBindings], + ); + + await expect( + verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + proof, + badBundle, + ), + ).to.be.revertedWithCustomError(mockRegistry, "InvalidFoldAttestation"); + }); + + it("reverts when signatures are bound to the wrong verifyingContract", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const wrongVerifyingContract = "0x0000000000000000000000000000000000000002"; + const { ordered, proof, bundle } = await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + wrongVerifyingContract, + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + await expect( + verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + proof, + bundle, + ), + ).to.be.revertedWithCustomError(mockRegistry, "InvalidFoldAttestation"); + }); + + it("reverts when attestation and binding counts mismatch", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const { ordered, proof, attestations, bindings } = + await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + await verifier.getAddress(), + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + const shortBundle = ethers.AbiCoder.defaultAbiCoder().encode( + [ + "tuple(uint256 partyId, bytes32 skAggCommit, bytes32 esmAggCommit, bytes signature)[]", + "tuple(uint256 partyId, address node)[]", + ], + [attestations.slice(0, 2), bindings], + ); + + await expect( + verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + proof, + shortBundle, + ), + ).to.be.revertedWithCustomError( + mockRegistry, + "AttestationBindingCountMismatch", + ); + }); + + it("reverts with typed error on malformed proof encoding", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const { ordered, bundle } = await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + await verifier.getAddress(), + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + await expect( + verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + "0x1234", + bundle, + ), + ).to.be.revertedWithCustomError(mockRegistry, "InvalidFoldAttestation"); + }); + + it("reverts with typed error on malformed bundle encoding", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const { ordered, proof } = await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + await verifier.getAddress(), + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + await expect( + verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + proof, + "0xabcd", + ), + ).to.be.revertedWithCustomError(mockRegistry, "InvalidFoldAttestation"); + }); +}); diff --git a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts index b7639aa5c7..baf03c8bf9 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -162,7 +162,13 @@ describe("Committee Expulsion & Fault Tolerance", function () { const publicKey = ethers.toUtf8Bytes("fake-public-key"); const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x"); + await registry.publishCommittee( + e3Id, + publicKey, + pkCommitment, + "0x", + "0x", + ); } return { @@ -336,6 +342,9 @@ describe("Committee Expulsion & Fault Tolerance", function () { ]); // Lane A: Slash op1 with attestation from [op2, op3] — active 3→2, still >= M=2 + // Evidence is the preimage of dataHash; the contract enforces + // `keccak256(evidence) == dataHash` and equal dataHashes across voters. + const evidence1 = ethers.hexlify(ethers.toUtf8Bytes("data1")); const proof = await signAndEncodeAttestation( [operator2, operator3], 0, @@ -343,7 +352,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("data1")), + evidence1, ); await slashingManager.proposeSlash( 0, @@ -406,6 +415,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { ]); // Slash operator1 once + const ev1 = ethers.hexlify(ethers.toUtf8Bytes("first")); const proof1 = await signAndEncodeAttestation( [operator2, operator3], 0, @@ -413,7 +423,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("first")), + ev1, ); await slashingManager.proposeSlash( 0, @@ -425,6 +435,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { // Slash operator1 again for a different proof type to verify expulsion is idempotent. // Same (e3Id, operator, proofType) would revert DuplicateEvidence — that's correct. // Using proofType=7 (C6ThresholdShareDecryption) with REASON_PT_7 instead. + const ev2 = ethers.hexlify(ethers.toUtf8Bytes("second")); const proof2 = await signAndEncodeAttestation( [operator2, operator3], 0, @@ -432,7 +443,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 7, // C6ThresholdShareDecryption — different proofType 31337, - ethers.keccak256(ethers.toUtf8Bytes("second")), + ev2, ); await slashingManager.proposeSlash( 0, @@ -541,6 +552,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { expect((await registry.getCommitteeViability(0)).activeCount).to.equal(4); // Expel 2 out of 4 — still have 2 >= M=2 + const evExpel1 = ethers.hexlify(ethers.toUtf8Bytes("expel1")); const proof1 = await signAndEncodeAttestation( [operator2, operator3], 0, @@ -548,7 +560,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("expel1")), + evExpel1, ); await slashingManager.proposeSlash( 0, @@ -557,6 +569,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { ); expect((await registry.getCommitteeViability(0)).activeCount).to.equal(3); + const evExpel2 = ethers.hexlify(ethers.toUtf8Bytes("expel2")); const proof2 = await signAndEncodeAttestation( [operator3, operator4], 0, @@ -564,7 +577,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("expel2")), + evExpel2, ); await slashingManager.proposeSlash( 0, @@ -689,6 +702,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { ]); // Expel operator1 — still viable (3 >= 2) + const evExpelOp1 = ethers.hexlify(ethers.toUtf8Bytes("expel-op1")); const proof1 = await signAndEncodeAttestation( [operator2, operator3], 0, @@ -696,7 +710,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("expel-op1")), + evExpelOp1, ); await slashingManager.proposeSlash( 0, @@ -705,6 +719,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { ); // Expel operator2 — still viable (2 >= 2) + const evExpelOp2 = ethers.hexlify(ethers.toUtf8Bytes("expel-op2")); const proof2 = await signAndEncodeAttestation( [operator3, operator4], 0, @@ -712,7 +727,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("expel-op2")), + evExpelOp2, ); await slashingManager.proposeSlash( 0, @@ -740,7 +755,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("expel-op3")), + ethers.hexlify(ethers.toUtf8Bytes("expel-op3")), ), ), ).to.be.revertedWithCustomError( diff --git a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts index e02050c8ca..eb4020eef9 100644 --- a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts +++ b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts @@ -48,11 +48,20 @@ describe("SlashingManager", function () { ); } - async function setupPolicies( - slashingManager: SlashingManager, - _mockVerifier?: MockCircuitVerifier, + function buildProofPolicy( + overrides: Partial<{ + ticketPenalty: bigint; + licensePenalty: bigint; + requiresProof: boolean; + proofVerifier: string; + banNode: boolean; + appealWindow: number; + enabled: boolean; + affectsCommittee: boolean; + failureReason: number; + }> = {}, ) { - const proofPolicy = { + return { ticketPenalty: ethers.parseUnits("50", 6), licensePenalty: ethers.parseEther("100"), requiresProof: true, @@ -62,7 +71,15 @@ describe("SlashingManager", function () { enabled: true, affectsCommittee: false, failureReason: 0, + ...overrides, }; + } + + async function setupPolicies( + slashingManager: SlashingManager, + _mockVerifier?: MockCircuitVerifier, + ) { + const proofPolicy = buildProofPolicy(); const evidencePolicy = { ticketPenalty: ethers.parseUnits("20", 6), @@ -76,17 +93,11 @@ describe("SlashingManager", function () { failureReason: 0, }; - const banPolicy = { + const banPolicy = buildProofPolicy({ ticketPenalty: ethers.parseUnits("100", 6), licensePenalty: ethers.parseEther("500"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, banNode: true, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + }); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); await slashingManager.setSlashPolicy(REASON_INACTIVITY, evidencePolicy); @@ -457,17 +468,7 @@ describe("SlashingManager", function () { mockCiphernodeRegistry, } = await loadFixture(setup); - const proofPolicy = { - ticketPenalty: ethers.parseUnits("50", 6), - licensePenalty: ethers.parseEther("100"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, - banNode: false, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + const proofPolicy = buildProofPolicy(); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); // Set up committee membership: operator must be a member, voters attest the operator is faulty @@ -515,17 +516,7 @@ describe("SlashingManager", function () { mockCiphernodeRegistry, } = await loadFixture(setup); - const proofPolicy = { - ticketPenalty: ethers.parseUnits("50", 6), - licensePenalty: ethers.parseEther("100"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, - banNode: false, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + const proofPolicy = buildProofPolicy(); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); // Threshold is 2 but only 1 vote provided @@ -565,17 +556,7 @@ describe("SlashingManager", function () { mockCiphernodeRegistry, } = await loadFixture(setup); - const proofPolicy = { - ticketPenalty: ethers.parseUnits("50", 6), - licensePenalty: ethers.parseEther("100"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, - banNode: false, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + const proofPolicy = buildProofPolicy(); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); const voter1Addr = await voter1.getAddress(); @@ -599,6 +580,8 @@ describe("SlashingManager", function () { ), ); const deadline = ethers.MaxUint256; + const evidence = ethers.hexlify(ethers.toUtf8Bytes("invalid-signature")); + const evidenceHash = ethers.keccak256(evidence); // Sort voters ascending const sortedVoters = [voter1Addr, voter2Addr].sort((a, b) => @@ -628,10 +611,12 @@ describe("SlashingManager", function () { const dataHashes: string[] = []; const signatures: string[] = []; + // Keep `dataHash == keccak256(evidence)` so this test isolates + // signature forgery and reverts with InvalidVoteSignature. for (let i = 0; i < sortedVoters.length; i++) { const voterAddr = sortedVoters[i]; voters.push(voterAddr); - dataHashes.push(ethers.ZeroHash); + dataHashes.push(evidenceHash); // For the second voter, use notTheOwner to sign (wrong signer) const signerToUse = @@ -640,7 +625,7 @@ describe("SlashingManager", function () { e3Id: 0, accusationId, voter: voterAddr, - dataHash: ethers.ZeroHash, + dataHash: evidenceHash, deadline, }; const signature = await signerToUse.signTypedData(domain, types, value); @@ -648,8 +633,8 @@ describe("SlashingManager", function () { } const proof = abiCoder.encode( - ["uint256", "address[]", "bytes32[]", "uint256", "bytes[]"], - [0, voters, dataHashes, deadline, signatures], + ["uint256", "address[]", "bytes32[]", "bytes", "uint256", "bytes[]"], + [0, voters, dataHashes, evidence, deadline, signatures], ); await expect( @@ -669,17 +654,7 @@ describe("SlashingManager", function () { mockCiphernodeRegistry, } = await loadFixture(setup); - const proofPolicy = { - ticketPenalty: ethers.parseUnits("50", 6), - licensePenalty: ethers.parseEther("100"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, - banNode: false, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + const proofPolicy = buildProofPolicy(); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); // Only voter1 is a committee member, but voter2 also signs @@ -703,6 +678,94 @@ describe("SlashingManager", function () { ).to.be.revertedWithCustomError(slashingManager, "VoterNotInCommittee"); }); + it("should revert if evidence preimage does not match signed dataHash", async function () { + const { + slashingManager, + proposer, + operatorAddress, + voter1, + voter2, + mockCiphernodeRegistry, + } = await loadFixture(setup); + + const proofPolicy = buildProofPolicy(); + await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); + + const e3Id = 0; + const voter1Addr = await voter1.getAddress(); + const voter2Addr = await voter2.getAddress(); + await mockCiphernodeRegistry.setCommitteeNodes(e3Id, [ + operatorAddress, + voter1Addr, + voter2Addr, + ]); + await mockCiphernodeRegistry.setThreshold(e3Id, 2); + + const evidence = ethers.hexlify( + ethers.toUtf8Bytes("mismatched-preimage"), + ); + const proof = await signAndEncodeAttestation( + [voter1, voter2], + e3Id, + operatorAddress, + await slashingManager.getAddress(), + 0, + 31337, + evidence, + ethers.MaxUint256, + ethers.ZeroHash, // deliberately mismatched vs keccak256(evidence) + ); + + await expect( + slashingManager + .connect(proposer) + .proposeSlash(e3Id, operatorAddress, proof), + ).to.be.revertedWithCustomError(slashingManager, "InvalidProof"); + }); + + it("should revert if attestation deadline has expired", async function () { + const { + slashingManager, + proposer, + operatorAddress, + voter1, + voter2, + mockCiphernodeRegistry, + } = await loadFixture(setup); + + const proofPolicy = buildProofPolicy(); + await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); + + const e3Id = 0; + const voter1Addr = await voter1.getAddress(); + const voter2Addr = await voter2.getAddress(); + await mockCiphernodeRegistry.setCommitteeNodes(e3Id, [ + operatorAddress, + voter1Addr, + voter2Addr, + ]); + await mockCiphernodeRegistry.setThreshold(e3Id, 2); + + const latest = await ethers.provider.getBlock("latest"); + const expiredDeadline = BigInt((latest?.timestamp ?? 0) - 1); + const proof = await signAndEncodeAttestation( + [voter1, voter2], + e3Id, + operatorAddress, + await slashingManager.getAddress(), + 0, + 31337, + ethers.ZeroHash, + expiredDeadline, + ); + + await expect( + slashingManager + .connect(proposer) + .proposeSlash(e3Id, operatorAddress, proof), + ).to.be.revertedWithCustomError(slashingManager, "SignatureExpired"); + }); + it("should revert if operator is zero address", async function () { const { slashingManager, proposer } = await loadFixture(setup); @@ -734,17 +797,7 @@ describe("SlashingManager", function () { const { slashingManager, proposer, operatorAddress } = await loadFixture(setup); - const proofPolicy = { - ticketPenalty: ethers.parseUnits("50", 6), - licensePenalty: ethers.parseEther("100"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, - banNode: false, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + const proofPolicy = buildProofPolicy(); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); await expect( @@ -764,17 +817,7 @@ describe("SlashingManager", function () { mockCiphernodeRegistry, } = await loadFixture(setup); - const proofPolicy = { - ticketPenalty: ethers.parseUnits("50", 6), - licensePenalty: ethers.parseEther("100"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, - banNode: false, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + const proofPolicy = buildProofPolicy(); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); const voter1Addr = await voter1.getAddress(); const voter2Addr = await voter2.getAddress(); @@ -894,6 +937,95 @@ describe("SlashingManager", function () { // banNode=true → auto-executed → node is now banned expect(await slashingManager.isBanned(operatorAddress)).to.be.true; }); + + it("should propose slash via DKG partyId attribution", async function () { + const { + slashingManager, + proposer, + operatorAddress, + voter1, + voter2, + mockCiphernodeRegistry, + } = await loadFixture(setup); + + const proofPolicy = buildProofPolicy(); + await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); + + const e3Id = 0; + const voter1Addr = await voter1.getAddress(); + const voter2Addr = await voter2.getAddress(); + await mockCiphernodeRegistry.setCommitteeNodes(e3Id, [ + operatorAddress, + voter1Addr, + voter2Addr, + ]); + await mockCiphernodeRegistry.setThreshold(e3Id, 2); + await (mockCiphernodeRegistry as any).setDkgAnchors( + e3Id, + [0], + [ethers.id("sk-0")], + [ethers.id("esm-0")], + ); + + const proof = await signAndEncodeAttestation( + [voter1, voter2], + e3Id, + operatorAddress, + await slashingManager.getAddress(), + ); + + await expect( + (slashingManager as any) + .connect(proposer) + .proposeSlashByDkgParty(e3Id, 0, proof), + ).to.emit(slashingManager, "SlashProposed"); + }); + + it("should revert if partyId is not in stored DKG anchors", async function () { + const { + slashingManager, + proposer, + operatorAddress, + voter1, + voter2, + mockCiphernodeRegistry, + } = await loadFixture(setup); + + const proofPolicy = buildProofPolicy(); + await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); + + const e3Id = 0; + const voter1Addr = await voter1.getAddress(); + const voter2Addr = await voter2.getAddress(); + await mockCiphernodeRegistry.setCommitteeNodes(e3Id, [ + operatorAddress, + voter1Addr, + voter2Addr, + ]); + await mockCiphernodeRegistry.setThreshold(e3Id, 2); + await (mockCiphernodeRegistry as any).setDkgAnchors( + e3Id, + [1], + [ethers.id("sk-1")], + [ethers.id("esm-1")], + ); + + const proof = await signAndEncodeAttestation( + [voter1, voter2], + e3Id, + operatorAddress, + await slashingManager.getAddress(), + ); + + await expect( + (slashingManager as any) + .connect(proposer) + .proposeSlashByDkgParty(e3Id, 0, proof), + ).to.be.revertedWithCustomError( + slashingManager, + "PartyIdNotInDkgAnchors", + ); + }); }); describe("proposeSlashEvidence() — Lane B (evidence-based, SLASHER_ROLE)", function () { @@ -1188,10 +1320,12 @@ describe("SlashingManager", function () { .connect(proposer) .proposeSlash(0, operatorAddress, proof); - // Cannot appeal proof-verified slashes — appeal window is 0 so it's already expired + // Cannot appeal proof-verified slashes whose policy has appealWindow == 0: + // they auto-execute inside `proposeSlash`, so the proposal is already + // marked executed by the time the accused tries to file an appeal. await expect( slashingManager.connect(operator).fileAppeal(0, "Cannot appeal proof"), - ).to.be.revertedWithCustomError(slashingManager, "AppealWindowExpired"); + ).to.be.revertedWithCustomError(slashingManager, "AlreadyExecuted"); }); it("should allow governance to resolve appeal (approve)", async function () { diff --git a/packages/enclave-contracts/test/fixtures/attestation.ts b/packages/enclave-contracts/test/fixtures/attestation.ts index 44ef21a8b6..62efafbd9f 100644 --- a/packages/enclave-contracts/test/fixtures/attestation.ts +++ b/packages/enclave-contracts/test/fixtures/attestation.ts @@ -29,8 +29,8 @@ const NO_EXPIRY = ethers.MaxUint256; * Helper to create signed committee attestation evidence for Lane A. * * Returns `abi.encode(uint256 proofType, address[] voters, bytes32[] dataHashes, - * uint256 deadline, bytes[] signatures)` with voters sorted - * ascending by address. + * bytes evidence, uint256 deadline, bytes[] signatures)` with + * voters sorted ascending by address. * * Each voter signs the EIP-712 `AccusationVote` struct against the * `EnclaveSlashing/1` domain anchored at `verifyingContract`. This binds the @@ -43,9 +43,10 @@ const NO_EXPIRY = ethers.MaxUint256; * @param verifyingContract - Address of the SlashingManager (EIP-712 domain). * @param proofType - Numeric proof type, mapped to a slash reason on-chain. * @param chainId - Chain ID for the EIP-712 domain. Defaults to 31337 (hardhat). - * @param dataHash - Witness hash. All voters must sign the same `dataHash` - * or `proposeSlash` reverts with `EquivocationDetected`. + * @param evidence - Evidence preimage bytes. All voters sign + * `keccak256(evidence)` as `dataHash`. * @param deadline - Optional unix expiry. Defaults to MaxUint256. + * @param dataHashOverride - Optional negative-test override for signed `dataHash`. */ export async function signAndEncodeAttestation( voterSigners: Signer[], @@ -54,8 +55,9 @@ export async function signAndEncodeAttestation( verifyingContract: string, proofType: number = 0, chainId: number = 31337, - dataHash: string = ethers.ZeroHash, + evidence: string = ethers.hexlify(ethers.toUtf8Bytes("lane-a-attestation")), deadline: bigint = NO_EXPIRY, + dataHashOverride?: string, ): Promise { const accusationId = ethers.keccak256( ethers.solidityPacked( @@ -98,6 +100,7 @@ export async function signAndEncodeAttestation( const voters: string[] = []; const dataHashes: string[] = []; const signatures: string[] = []; + const dataHash = dataHashOverride ?? ethers.keccak256(evidence); for (const { signer, address: voterAddress } of signersWithAddrs) { voters.push(voterAddress); @@ -128,7 +131,7 @@ export async function signAndEncodeAttestation( void abiCoder; return ethers.AbiCoder.defaultAbiCoder().encode( - ["uint256", "address[]", "bytes32[]", "uint256", "bytes[]"], - [proofType, voters, dataHashes, deadline, signatures], + ["uint256", "address[]", "bytes32[]", "bytes", "uint256", "bytes[]"], + [proofType, voters, dataHashes, evidence, deadline, signatures], ); } diff --git a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md index c289ecab3e..ee04bc4f71 100644 --- a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md +++ b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md @@ -6,7 +6,7 @@ Golden `dkg_aggregator` / `decryption_aggregator` EVM proofs for ## Automatic refresh After an insecure benchmark run that writes -`circuits/benchmarks/results_insecure/integration_summary.json`, +`circuits/benchmarks/results_insecure_agg/integration_summary.json`, `circuits/benchmarks/scripts/run_benchmarks.sh` calls `sync_bfv_vk_binding_fixture.sh` and updates this directory’s `folded_artifacts.json`. diff --git a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json index fe5f7e9a14..d6d7a20871 100644 --- a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json +++ b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json @@ -1,10 +1,10 @@ { "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000001104c69c76ea8f5f6000000000000000000000000000000000000000000000005c85075d6baa615c900000000000000000000000000000000000000000000000eb6de0b1c015f0f2a0000000000000000000000000000000000000000000000000002a0b5c2857c800000000000000000000000000000000000000000000000036176084b0205127a000000000000000000000000000000000000000000000004549377f025fd0f40000000000000000000000000000000000000000000000003ff07c8a94fbdbbd8000000000000000000000000000000000000000000000000000185ecac0421330000000000000000000000000000000000000000000000044f1f17a53eba29b1000000000000000000000000000000000000000000000004096147c1d40da636000000000000000000000000000000000000000000000007c2eaade9aae157c100000000000000000000000000000000000000000000000000023ccf2dca92b000000000000000000000000000000000000000000000000e02d12f90edb8cac6000000000000000000000000000000000000000000000007c223713eba54496600000000000000000000000000000000000000000000000ec928a9ef7dcf2da9000000000000000000000000000000000000000000000000000282e9bb83bee61a05d33c39ba3812376a494cad4c46e07e18763a4c0cc37c7ac2f3635530c6110f00693d937276e524dafd5fffac373d40ca9ac38bc4003aa97fa25c28eaae471e6801e127832204ea5a325f05bb8550bd90338e17663499f9908e07c1a27a4d25db068d58486c5ed1d571b154613b2a444f9ef333d6490c341531f43898470028e20e0d46d87d6e6a8c672bd88dbdbae7c0e0a9d06da0323fdaf7ee61ceb7611d45afd3c8ec55431dab1b693939c2a7f313b832a28959c3ae3d30629655cc051ff1a5efe96161c60a46f240cb6ca1a531353aec9b48b52ad525bf6481f17e0923676a74fc00629f08348ca4b4467e32b7907ccf75e78538096134b580c00ad016d8f24d8b2251a384465ecf7da74301f64008beb9c8b71af609a0085afa8bb10891080d365806d9ed9f21901108254290c057a4b986687bebd3eae86d9d84d52ba59894f06a73d164ff99fcb0d13873279ce4f307eef63d2c0ba38c4624e95e009c113dba40ba429e2620e10806283dd5fad8df1b6a85051c1f7167194c70620f370484b9bd06cf6e6199ccd69d2c0e0db004474b4ffc62084f7697d21fc04f1518380c30df7edd6752046611f209863dd69ccbd4af39b35e05dbaa35d7aea81dac6609d0b9b649962dd23bd220709758af7743abf5395724f809100dfd19cb13ee772bc80efdcb985314bbe58feda8ac8cb79523fe9ff183ebf3e6835eff81084ebea45358443421fb0f2d31a11171c0537eaf216aa68a5bfefa0d02b1c5b92a10ac9238f294ee18007a6f18f72214951c398ce84c3616bcf3c79e0f2e2f022a0c90527061c47a42c43cace4fc879a5a326863a6f82d9c6bb420290aa900d8244a98b1668bcfcb6023d101fde9f2fa579bbd8947480f7058466d6000b38c5c1a4fd3aa5794aad136c957e92be80cca60120b6d839e54f1036f9e857e14746f2433e79c2a2df9f7ea53e60405fe812eb086dfac1f0b0c638610dbdd208a28f2260c558292be011ce1ad7ea6fc1a18428a3199764be0e5e4169e261b655ffddd06ca1104a26cac71683ee832dcd3cac2ca05e81de30bed5f8b67ce620c6f88f62d90a64a9659cd90458edbd62bb6cc9f8d942669ccb243386ee53c938746a2462ab0830a5ee749ccf6259eb0ddd308297e3093c1e2bf8454dd5e900345907a8608b9832d8e40d08cb3dbf14939398e8c032b1baf82f3b45da26fc2aa8a69639d1379060a52801809840916499ab8679e0af7fa2b5e549f58ddfc8c60090ad5d42fcf59ab9661b29f6aee13a20e279602925e6fffb2328ef1334585e819ea8002085571a91540444e60b8c9350331f5cdfe8ba889466f4b26343d3b179b436d1015fdbeb6b1586fec95a850abe17e3d23dbb519b068c9c59ed740833e71319db516be4631e64a10edbbdfac8daafe9004a1dfb5631249e558132163eb6429c715143679b05e2edc9c236a9a7ca760761c3a117bb03755bf8d04aefc7742ab0ba811710087242ad211047ff4f6b9c4f8faa4640f53721a400815c301a8b079d1ca2b7184bc65e7bc3cd2c380df2376ec233411c4d429fb12795cdaa8b932bff94628703868796573c773ba5a60dbe2dcfdba86fbf73e2cbabe306511dd6f71509607ebc04687500585f37d8135ebac5386803439c01b36e449d3b029c1576fc2f422160f4fa03d8f11c89e134103a7918939563aad5285049c66b3479d416852b11896f8b31c6d91580c0cc03bd04ef40a9b201d686cbfec6170ee5a1aace7c7f2248c548038c030a7c267fe72915cd397e273c42561e11410672a1e41f71827eb247b679c4eebf11bf064f256f418ef2364b7eaa29fa9cfbf018eef1952daebd915d423652908cbd25b28b3b07d28cc918af5ac73226b02d6340d02ee74f643db15c870a4de33c870580fb55142ed86a7b1efb02d4f5d1d6e074137126f61818d0dce8e49b8aff13f2e80fa7d585aff6147ee7265bc92df749635d9cd77bfb87508e4dd08b4f437bb59702e34ec8c390bf6e7455a4619bb3586688b16e1045a9c0c8d0b760cf3d11357e9840fd3e702cce25045c7835bb1b0ce6275af5571d18d1e90731123ccd8bb5b7550b8ba901f3e94958acb3e9583067cdaf68cd913726817755888db2a7a28e1a4748b9ede248fe0ef76aa51bf8e5eaca3049ac9dd7984241212bdbe5b3d0d4b586a7f468056893fbdde4c94203e8ff8238fa1a028ab2b059669f73b36030f5205fcc5958017fd26f9fa8d827b6cf11aee31fa922e8f0c21c5cccf059ac7d29e54a4df6f1e43b8a4b22689859e8569dc98990b9fd1d1b2222e19e76cbb31fff18fcf40798cf42482bc063795fa19dfe65eb6e9a570b4c41e85f9c94ff56701adabc0cdb76def2f5770270aa236e1be1c18ed64e51cdc911a1133e096a0272e0675c680fe24f2cb4f9a201401977945b920b02e60c84f3309e8ba8688c6e982bc9357327d438c5d34986a3ee5c88fc5886a4c66f1d14ef625d0aa01c82cf320cb1543887eea14b4481733f213beaef47ad92251eb2910bb1c23b4c930200102e6ec848b5470cc88a0204f6d4a9aaa1cb29e808e3cf2cc7622ea3e6a331b26e1d2a78f90b99066780df63afffc89bb080ef954828fc64a9b0e589c3d02f1f8f8c03919178dcd7138e1381f54749a684c6c7f852b0e85251427b6dad8334cd11f9f2e16ab2d7654d1dad887e5dce9d872887c6dc9d07f45481835c2ae47a1c0c9b680a009ce8218506f77c3b8b62654b8fb93043f60dca8992c4ca79d52dc8431a240cc5d01a027438fa19c91f638b7cddd6db1a1f6faedbe1c7eda2cda8637e7e0a2f49d2ad0109f7a5bbbadf6c8478ba78e716fab26944e182e437f00afe8873f1030439bfed6f6bb3abc39bd1882eb2b6eda0078c680e924d942cd5b55449783c3a8f192e24aaa6c305167959fe1cf58e9065a7784325c2d0fe06c3e064f99ebc1570a82cd43c33ea443971e0f434ac11883fe84461b9821746609c618a3362bf051334253a22ae38e9b6773a528204c0797297835f3f61549c8cd8ad16bb415ada181c52d843d558dea5cd548c3823ac4e3d6df1838e0073102582f62d7dc0ac66710e9d0e8826ef77755403b7f12a2863faac6dfe4cb1914e29db1b956406e9f46ba2653617ac8ede05dfb69035dca3ee5c2d00d3a612ec25b4fe71e06659ef23bb53a839b6ee48c4e38207109e68d3e073fef16eb062dcc3a22a15afb381581cd7e9db2c78f2b801ef7104a5ffb41dce92caeebf6141190682aae5ce74e6b6adc7c901eb6db76c548746b9170a1c81930149032b19c0c0ab947223f7d82d1f7fe5930eb75cb6dd0bf5394a906376146da02731747ba17ee40b5061140ddbffbf7b01d0e6661cb6d3cca9eeca0a0c4640c5d61c72d9819f1b54c8878b1cacc853f1ca2e3ef455bd082bf5bf60e4b13ba7569e7804d5b1442193595af32b92e91b90c7f7c9999f7c665a9c7a7f5911d73d415c3339c6f0a968a68b6475c1dabe40dd46087630e1fabf2155f2017e4fb42180a3319e7d7063c027b22b6b8389f83c9aba5a25fcb0cee256c1c16760db9bdec0c82d90800249d026ad7e4fa1f8635c4ee66ac2ecc8ac01ec76d44981128e4000cd5a529b4011b6145ece1a1d5404a453d8f28fb6ecedf4584fc8a952cb6f9d6df93cafcd21eac2a8b63973d1643a07daf8ac82dd5b054c0d25445e09978693ac63ee2de6516fc2b9c0d2c59aa561b6029edfe22e85ec79edb34bdc7617ba11cf3d4c0f02b13c2eeb3c0faa2fd0592f8303ed4bac9e9f81e7c627493f4a334c539b8acd96f02bc66d56f149f0139979ef02b7f584ae703925e664182e410729af2b9675cb406169d27e032bfcc6e540bd4b7b1b11054f83ac31d5c5013e0ac3f6d6e6a97ec1b08b22c0d8fcec02e23c3d2f56227fdf155a9a7c363854deaecc5321bc94fc52e50df9ac72c04b4fb74cbf7cd86c427f9049ea0123dbabed674a4e2de9652290ddbf03a1835d5a92ae4ef3237137432ccc8146c33fbd50155eb4e68cabd0208196843c201f2ed0ff53d389b7845fe62eb1e873c6f5c9c1216ad8ffdfeadfb1718e74c50fe500225ee24b08374d2a8b7fc17d324f50220961759194b39f844b12739e80621a5960316675859bed49ede417bf5a5acc2e4b364b70bb92683986c1df944a9a3b8cc87e3951e156af6abca648b28be2b74e6df59df0801edc236b129ba40af8c1c9f93b26fd9a45d102fa7bf90e7f51f14dd59d241c4327642134210376431209d2593785a6a4a6f042505387ab87daf614bbafce0a71426a7e26d2ebe8f7a36e23272d68e4396afe0eb899fe01103e5190ad987834ca9971bed9c1030fa67d27207d4a97e2583383f5904f0a0ef868a6b8a0a2c85fe1c9554e620130e6c08293a578b5a28ea7c0021ceb00c5e4e171a08f2097216d3dced274a6407868f61297f9fe9e95cfa52375e1390c2a034d849bbce454638828ee58aec0b0e171dadb3c2ba34e649b6df427a1a22ec81e2406de5d9d3db670db9198fc1070ef752419ede5f9973c2105bfba9ccb21d5e02ea1e13024a8072d8d2816018970bb8e226425c4c82771c7400941b55dac1a82c6ee13100d8bda00bab5a6d4e0111f101af580b036e177b7e88d70ebf1f57d3d9869f488de7ef2e41923a71cda5153c0f99aa741088d700453e078bda0ac7cf72c4d40e5755f51e9f972d5193161ffbef1f56c0eaf0507bfe67e000aa14c77e2fc597669522fd7553bc3a340e622190378956119d6213836b644a25648d9b3463ff256fa07f431fb1254f38a2540d7d8d1ba4d4000d5470105399683fa97f32adc17549946b7d4e33a06085ea451bb9b0d600fe3202fa84f96bbe9895918438d9d23440f5d086cb4624eefc7a2c09554cefe010fc8412f7dea624ecba0b778302ee742b84e396dc43d3c76eb2b10567ee21da53b51452ef279344c3f4cf1797576436ef727f496641913bc0f28b2dd03f71184034d502788de0d7d8caa15ab98c454d2676e297017823c35c061419bd9a63ba9deda91b4a7f5cc27883af1ce981a40998cb00af959ef782a4971b2bdfd91e38d7045ff5b399c5c097e014ee9959edff45d11affda024527b1c03417b6609dd14487de0f2ab328f2c7066e2fe3d18fdad528fa097746a24139bfcd1361368052ae8ebd22969612d971a162c5cd161da46aefa32d4b540937b80ee404eae6eb13a39ce56f8fff92b54da51403fdfcf7946dd8a43ce26cdccee842af2f6eb15827f305af101464d910fb943488e0e5ba33853c6206eddceb9e2575bf15ab59f5b519e3023a368f7c1584b1f9e4e185f05cdf6b4ae976f075d4db94882711c2ab8e50b028f8f181b95604247e18cefb32c187176e0c4264b9d9e78f7c053277dd97cae1eb03dcf68ccb9c14d6f77256486d60a6ef3c8dfc54254ae5d317284ed44a8c2ef6ddf4aec97a529d841a43fdf3bff0d07776383574af343a6b2504328a61ae7607b1e792ad94fd63e0f5f7a714febd8866ecf4e93be767a16f2c6a4302ed3c88f3e59bf92c5343617ad9051655cd702ff03e0a52af7f0759fa18826dd0d6c0d6b9b58a943c485c911cc2e83d683725ff800e4f44dea9e5747c064f56dec7c38e976a8667949d972a1e8d5cf265e4f07d86e2bd4c2706fea5e50780f6bbcf004fad574131b2a5d060402b25a79ba39adebc44adec286e2641d02c06eb8264faccb1c5ba4cfe130df3ea472e3ad9df249f14eb4db5f2a7b1bc271d67455ab068c58b17a0eb2445f60d9788b899e3779594819895fadc61cc2e080daff29cee7c04c152928572b235c790f229121bf087c155efff507d086f273f11769248554252c8d5972cc79eb93724ca839565986fd0d23149a7d428b2eb18206374aaf52de2629a8215536912ce04303d8308cbe65b8b76a963b097519a531615325c2fd7a1cdd84daeaea5f0951ba4421342db3b0225bd35b659e31cff10180bd6dc65a34a851c7c9d943c9a600c76aa179626a52d8e9f77fb03b5f03100037dc56ffa38081b2a6db99e9299075f3b2c4cf6039d9586cbf123bb759bdb130475385704108ec22012b4742792a05037da6aaa56e84da60c467ea7d3a908e81d7ecf0f95150be7f4d5dec9a1dd9dc832495542d1c376395c5e7aa334e020a21b28b9245119102fd119e81c340311a9ae3749ba3384ba0f1204f6293e3937dc0eeb2fd7a2ac724da6224dfd2dbec16d3a3af4c70ef9ffc653a52eab8d1693a61bf5633124ef3a8eec373e13373abc59d5993e616593eba7dbb5e248ff802881102c6eb060d5d2b8968e9eef372763ca5183b873e5a581e9e865f47b6028bff71c19cd3c08fd68a23f0d9c99aab607387b9c6ec678f9455616311768fa832a5204da68b4a1d18935ff4523e6cf72b1850132058c7f150c2e126196b6723f19ad2fb8db2bfb259f034348221b3d3f87b85780619a6d54e67cf0a55965862b7fe52e23144adc595bc2be1a48f1c1cc41b41dd9da0dbe5adef141730e340d04585616328ade11c06b5003e195f1e3dab9003fa8013ffd3f7e6a67c1f51ebf65dad418a861e2f11f2ffe125ce67efb0eb74f33bd131b5a5d11b6446a8b3fa207ed8b2285a3475847e33e625ed4e7d6533778b595aff6607586bb0412d8210fe340552e3583fceba101476b4fd039726118ac87f80df3cba443836dc2a53fd6fcd34e0e18d5690c79a9e9db8026bb60f070b81acd43c1e5f7030ea7dd2d61bff42ea430279ed1125758d018a344fb17c2592a3d2c34594303a65937825b43c263ceff01c029ef10cc78125dd9f4a55a19c460e1ad391a367f976c8d14a2fb91320efe28825a32c3b76a082a9e42428bac2e25c4892681860db150836148b3b1fdb13d1f64fd78bb318ce7387682b34c821bedafa127f2556a9c2687178ebe195756f609fe81400fb471b4fb1d59f727b378ed82d87dcd5d496097eb293d973564d398058ff3208fb5c9cd219328dff54018be2374f4a66fa489fcdff636810ec5288a0f1428e907d19a12444d9075431e049e594078e1d039e1260075278fb374a93e0105ee257808016fb5c76dee7c048012653bed21b536bffc786e8a1d77a2b8290b41964caad2025868bca96f50308b0e8d7a814ec83b900c8f77583e86bae18a18d2d335215664076fbe4a32999c11aad848aaa1df092c39ba98380a3439943e1ee02ed6a5098b95951eb7e0447b7df1eef98bc02b65dfe974bb92a4ffccc41c2983166df1755e1dfbd5545f0d62e8a27daaead2c734a6e61a196f7c253b967c1528ef00362014c7cae847514cfffb599397f6fe6c6ee72025414d2908b47de40a84cb4e3c3424ba0329707761ff4b3262835631ea1a3e164fedf5077ad2ad20039921b628ef95d8480eb14371130e00af763739537e2d02d6f125589f25eb850ec1140ed345fc070944b3846169aaf337e145e157dba7bb5cc8ae1919e050e01864dcdeb4f64782d440a6e643bd1f58b00fa425237d8a1f8b9a014d7198127601a587ed01e73a590f2bb886b1526b9aa2f577b51ab869e9e11fb795efaaa6c31762c2ab563613d28bfb2c3d59f769eb526bff8eb35ce7a97fb655447093663f1d1095d270f54f28866027ea95f63a6e5715618c99e7e08f17948a1e8c65d0f223eb8346f3a9ca6c43ab9669df0c286376cb610b5a073c5371c2c168935bc1a82162a0e476196e2da58d90892d41a9a9ba1d51aea5d2e8fe1d3554036501ad561776fe8203c0febc1bc49c023b4f2095f7ab99dc9fa4a09c70050ea6c1a6f93d04e66dace55265374deb889c25cf2a56f3cd964ae8d3ac45ee95781743bf701a13b4cb3c6948375a09050916d900fb3d0811a077bf61fad07727b53c2564dfe216679c8ff255da315e1e475b866a908dbbbd515d620c7182b849e8863869ddd808902283f89ce762a42304280edf68f9a76b67fcb61a7a54cda8575384ef380b24d73b1e75b50a6174ae6ab664e0a75f91d4acd60402170a2bf967c9862f0ae40ea80f4d02e46dfc9d22f4c5d3358a3a741b39930b017558af7a8c9e268f37a00cd48b6a5afe6dc0785efacd85eacbf38ff3691f503bd723796d7a2b7a5505471c0a5dfa4b6eec5f56d4b7e4805da697d7a7e2878d717e21d742f9699ac0aa802f8b8c774afd950836e8ec22a520412abd775c4619aafa9dd53a9dba31f385d121f23710baecf82aadad99712d54fbbff07e19d618663216ebf13dd355389ed70ab297bb2e8f14c44d9db5aed00f4272956f940c814e02c329cac3b33e110f7b260c8b643c9225c725f638d415096330c953d46a6e6901548de47f050cea00921c406ed69b8affa8c49e5126b09b7407f1fe39e6c3a67ff985f44b4068d17ca2211a8e6004ac2ce57b738ea79b47a79212fe05f59fc333dfea5b973e0189f8711d2ac25b7da815377b5e0b79bb57edfeb0b3e249284b093f937eb3c8cd5dfc470e799e62210ee5038ca82113eb298d0a91ed7265809b0e3c41842327227bca7e179281876a2d730f88bfcde6a261d12b63b325fc4b8f8a07deaab29e073e24612e721bd4dfbc7a76064f51b97cb8c9aadaebc2219602c4018e412558f0c92cf50f0300b41c3af93217af662fae6c20aba5c5de25678a70e7548eed1465315e04260f3dcd91a642d67ec3157acf46ef336e01231612fd33a33d547b352838ff602202827e24bf1d82e874c4b2e56d6da2211d4f7ca4affadd0e17c9939bae8c861988b22b4b7bb9ef9dffa40b919b3a3182f30d07eb113eb168bd5c30bd8b206a032495cd76b9b5ded766b724b3e8a955b114b6bc8eb936462efea06162c87c2c1caaf7adc04449a6a314f788d3f2e7ad03a89388119434eb73467f6a22898b762192c947c632f83b1d839ac813849f7f22884a21461081a6d636bc2c044241732a226e71541009557abe207a50bb07ee6e5318c4ee2f10bb0bcda8e4fe00e6b71fc41469002dd8f72c043824f87eeaab33b31ac8effef573e9f15bb24f1af840107e0034eaaa8cce6143056e561c708175ebd2bd2b01f7204024529b36328ec107e1079b23231ca0576310db1dcddae6dfb2272b968d23ce655bfaa3b5b578bb051aa676102be135cff656a57bf369ca177e279e14f723c6d567cfa0cce54170301665691ed411acc68341b6e22d99e80c111504fb6e8fa565540666a6c139f21693e4c8d39fee103747a085926e9c36d672e0950b53e0168b556f7cb1a672ec12a0aa0eab6371061b8532a895927a22ff8cd0d1b0f4c4daa831cfdb62551d422abedad3879ba0e5d639f4ea58196f23087af2ffd496a7572303590001f57c652e0d004ca35307c2f8c99a335d6378a3770e5dea4aeea8e5a4493369f406fbdd1bb85993f3834486eb72a18278e27c76aa92986a4b9285143493f120104d628016e1f540e94796eb5b0014ee099328f84ceb614add86ccfbc550f7600f3413651c6d6c29222e50e8a15edef04969562fdbee5ffa843960d92d565d380cbe1def21d8212fb0ef618b09a78a0d3030ea274197521f239b7b82874d575222ec0ba81ee82dca840e340012136ef0b243eba0e650058aac4a5fc88ad5327ad848ecc710d71beeb1dfa88fb932d6125e181271335a9083d7fc6e13cdb92d1ea5b3d60912591a5d8eab8b8db19ca4336fc32c3147ffe8088684fdcaebf5f634cf5e1eab1b68a4d96dbdaa0afd40748e0b4a0ebfba0a627f30c9dbc621e727f7bef5e78e245f34dcd0a1828753e5723eaa14bb4313dbe15d5e76965f8bb765910f417ae403a931a5b653d4fbe31332fd04fc6e69626fb834cda6fe48674b98219ec25e212df043b86786dd8b06d34b0bb7e6cf4c5bbaa01158fc330b05fcee8b4fb9eed20ead836e6b8cdbc6cbc06457ac64cf3a72283181e35a9657ba381ed47169ef3f178a2f40907c36a2de63a99c8cbe92a23d9b10305c3003f5341ef1f9bdef0a99000586c10ea5861a049a13f7c69b0bf6526387aedb3952554218cb5ae0266b3d17107b45cca47a5078acf00a36247bd987db1d331c585953c27b3e7b68e34c5c086f1e38aef9ef67f6bd98ba574b8534c2f71e4ec038565f0b24c6faeeff168926ae5209a04acf180a8c7481af21deeb7a02886f61625e734604feae11bb965306c08ab20127db181eb638f57097f85600f251529faea4299afb46dc539ed447212bbf694211232606e3fc74627321d1188ba6d02f9e8269dd81843182e41575113b3b7b2c3b1a8698546d3aec50985c37a3184edcc69efc421fd3e4f939fde4182255831a27d9b5d835ed40def306d138225b855aa0b81e5707438d4cf7408725dd70cb357f50ec938767a6216c3c45ef2034d3fbc3c8555a67b7dc78b47d8628fd188cef3d2b8dfbf4fc38ef79f480b2efd8c4adfd46fb6205e251212ebaf31e70b2d8a82ef799db3afddc46ae635734ac0c700fe84891c053e63156981b0b0fdba632fd2f3c239ee4bd04254130f5d66e68515143c5f4d6f2b29e17bcb1a303aa93928c09c5312a4d16946121da973879c2af82cf71b14a2ecbeda0e5ec8b02462777d72e300a2f695fe1e4440becc12fea0e2ca14ff1862931e8175d31cb1c9f8844549a6ea22385d3a4f60cd14fd6425aec8890bde9221d42f60e54bca41eea53a6a00c6fe7b66f129dec025d3c05b1a7661322b803efc62b123918b9700799cf50fecd11f0787f2807bf01fbec1f9774eb693afa70d652dd59d292b74809f0b45fd23a36228017f8cfaffd39368d2317dfa86d5eeefabb69e13c7d7ca5128829e70548ddef6f0d1230f3674f752890c754e20a858ff699eb425595132818423036c330b0a4c86f547594d8e34b5abacc9933308d03ff4b5307c9f44b721d9e1d6655d4140a8adca0117a6ac07565e7e8887975eb3965583949cd93c18900bb301068bcbc64aa85a14a8ef8365ea3f57bbe465f80ed8c039a04a7c9e5da0e09ec9c44885c8ab72021e4f8f6c388240c577957dbdd7f64e929aa54c2960a071e8b4cd84f4f659b95605e75c42ca42547f2462b36f75b57972e6f9859d45f067723d028b166e84ad2a5d9921bf762376c3e90ae6b17bd87c27e0fc007a77d1f5f7d8b0028999ef82f084a250d8562793aa0ed27644ad82feb94e80d948a6f09feb51fd95f0632eb2b356c130b50c4f70fb0f967cb7eeddb222c546a5efb49050476a0d41bb023dc5d85b1cd7776f9a2c3c792be49704ef8e0bb09b9e6913728a0b8490ce3a1277aaa62aab1f07ebb41c1f395c34eb9e983884e8dd953bf4a0f0e353000cb746ce229ae769ee10c47f15ec40591d589d798d0c2bd2655751106774bd58d59720491a086b4721fe745209895969578d037e71ae982f8a9320322a859297e7a85a2002ebe2e883aaad92af39668cd0798c08b7eddd7d503b24b1ae2daa5c4eb25393ee6ebf4008bc4e0c072ca90b67a65c586fcd5550b29423f093d0da983505afaafb1eb0771e8e9e1cb3539fbe61f5efca2d4860b1fa1b36121e5effbfaf7611b946208ea448d990b3cfdaf50b40063af0b7cccab49f5c86b16506192e554f89afa326ed0528f6aa248eba2da1825d58628355393113a8fee286a02b1c4a452f8f15011bf27dfa4fd6832f09725a7219294e11954b957f9221166c24763a83391287fc3eefe14933c5c14f4c7d6937f7b75542347d04045a110890ee0f78b861b3af29a624e25b367017d565f468cddce5b7f0cf3c35939a710bf47754178cfcde50bc00ad8afdeb5f65ac80bf3130f8f916859fdd0d676b51a19030af15d64ddc9aefd991cd5cfa036cb80826b4373b9d7bcc892d88b7dc91054be1686426a50d43560b6f5a738e11ac395dab9fe8c808f8f8ceef391350406b72a33744c6e8340275bc6ba889bb2a39e4e44a5ec3608ba7e9ff3169958a41fdf27650f39023fd280fbcd8a14f01e01129e55d1f39690748e99810326fdcd20e052a932671c905b1eaa5c122a28ec8ee55385d4230903c923a10347dfdee01e17b2763b6a7060a5b8096bdf84673c406e1bbeb5fba30c109497c0aa3ce4d91814c363d790b240b21d0c46bca0a903c5fee1e5fd6e2278a36d7209bff11e701244171b2ba258a8c4b611fe9d842c33b90dc2bb751a4c9834b94ee633ff2f0c17e99d90e05e03e67dcc81f0482fe23b3edcf8af33abbd48817fcd2f01e5cbad02174e2613f854965fff32fcfe1b2ceb878c5e8451dd0aa5d75a96db1423038d224a2a1f5665b42ccf0cd2009d22d026f92aed1329fa0eeeac0bd789e8a094c82617ebca343afad708cbf7746a1a7b486be884219e41fc54bc92afa7c4fde27204e3b778f1434022e9a0f5af3cb796f94594c41fe3d1343ce8e95cfa3405e76024841490ba15b8d6e571c78b5fa9bdb4f42cf6939f0ca766f8a33f9af1388e9f0cb4e92a32b3f70b729d395a955e2080b6f0ef0d26e8692148295a7b56b5f6a31596ef09d919d4e9ff98704206e61a8dc0a9ce4662eaf6a10878f3180ecd6bcb0093e2dacd06fe78b044dd4e2f973281e38c793a05bc453015dd5d99c1adfa70234a32f957d93338fde57563ebaf78674ac0ecfd3bfe0a8293a8222791613d2c2ffeacc050d243fae78fecd4172ef8ac8d65605f37137ac2d95706f7fd8f331204bbed683e9949fba2e4bf9941b4939ee707c3973843138bebfee49358309dd60265751d08c2e979f56c6bd140921e7a66e34962c2e608d7326c40f95b797636285580e22fa6e96ffbdfa09b8be7d9c5def6bb69b24b832097fc8501a8ca707c0b7f05f0505eb62b7964b96d0c4913735b728fa4baf217082e8f47e2f582872412c7a05dcc806ce1d8cddcca1d989321c22e660aeba3d8945d7f932b6de9e6591b97a10e4e7f2ac05c0135756d3e162058d1b9e577760bfa12f779dc1b21c04300b8aa9e06aa51ffdcd11ff50c67a307276719bc15611fd86c8098faf7c829da2c4b1f4eb53f6fb595e1961d38512f3ce9827b9e4822b90fe6e5c0c52672cc1d012e591b62f714b671321c5ce756f826cdc2bd630a9a79272f7aa02baf56779d25d42ac69b671532b1d51d23e7c79160bc322015ca838b4e674eea5167aba83c218ea51067f7176b027556fd11238b4bcf8865d89a9cd0bb3e4f43477398ccf22aa043b2ff7867dea2472de15e42e20bfccd0fd02248e6f1ebf7f95edc2eb2312c0a6b7ad4f50c3bf03555011fdf65cd0e2fa85d3bb7df6dd924e7bb5794985118a1d10561c8f03828a912b04d1e50f8bde69235134a4d4ae2da48c118a5dce12e703a9115055cdecba78a75a6d10a5e56613e9dc3efd226cd0756e592f880642c9974ff67e8a91633f52d02e73ef44399b35cab4796c37156f0f9800b7a88660345dcbf63e44f1fa2d1f8eab547a67f70db529978f40217b5422eed2588a4ee300457f11907f9415e349dad8cb86d9910eebe588cc8c0cc798a522e39a669031bcc7172286c9537143a92c965481a89f3c4e860d5ca9824175d9c7a597ee7c61c115ec99fde4909f191e31613746673c5507ab6e01436e6fe5f07d8a929fef51ff7676cbb3def22e44a545acbb9aaee1773f706fc8684a7b465d2f92f8af7dc0a1800216db2c08004b42287c6df656f6b5dedaae8e1b9013c94532463df05952bbf330909de4d033727d32480cae73d2a2e9e50326fc605ae30c0c9f011128b1a678d3619cafef8cf5d5e07d61a849c05e91e9ce8290b8e0ee69c705736ed81117bbbb0fad9bfc93fbeda8ec8b9464853ee161b5fa9b5e954c36d10f7d2e01d0837f15100dc35eb17b2dad7ccd87b482048827c1716d510244380ac5b0934e3124b0552ca7a5902213329c982bf38782b0aa337a618d658aec9d01e4885977f1514eb3c815c8ac482050a2d19638a584180f196c16339e4652b1b7f1ad870891d63a0420da9390f9dc04588fdfb066906fbeda08ee28cf7f06541519f991aa7125c2099036a614739e41d2ee4266b3faec273304a8a7fb09758a64cdae16d281d06427553cb1acf30f1603889e575889f826a832acc44180f68c230647d971413a392f060a527885509f15ad448f8aece5aadc9836862138a8b6d3b52c33dd41faa36de1e5f4355825cc13a32d55f9d84a7337b691d12ab95d7eddcdd3f72a22ee84a7b8932c0aec98b47176220beadc273414c3a7d739e598b932603544fab03f191cf63a65ae49a832011f34c94d75bb99361b17399b27d77231128adfb33018fd3f19e9f95a38b4f4296e1b8ac28f6ee41814be442b889ffd99b621ce5bf1bf69588a55456a7f9911222a4825f68d22106aaf2ee07891bbcae65d952e75b2ce90f558af3f890bff0115d005d1e5f714935cb35aa8e12c3b0f194f53ba20427e5d13527c1a9f7563f61ae1f59a6dc16f2cca1df47ba4d47ad490a54d1a9af10e1bfddf43ad707f810b5b9b48a91b16d892c73403c3f5cd6670bf79e355e1d1cb721ea112226ce6a6d52283c887da649f153a7790ec04f440176987419b5421b7c694c646cbc5618e0f2e9d9ca5a10be1c9e007d519aebc3fcaa904cf4028d0f15c92504ec8655c6f5ec0bdac134b4194ab85c505cca657e295d2041c68be114b83a369cc7707cd41264238945dcc4d2581939579ebdc673ae8a5ec925776e28341d66f9f6eae02473c10b21c2f682ba22fd78f1b25895ac2adad8511422110bdfcc50e8b7e10c22d1061ca72d0945d2ae76eb874cca31ae41ce92e335e6732e0ecb8257b64e85774c95c601b69ccdd6cbdb5bce2bdb755e0f4309e735725f", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x000000000000000000000000000000000000000000000002b30b0403aeee459600000000000000000000000000000000000000000000000db8eebcfc671fdeea00000000000000000000000000000000000000000000000af8f618d2adad1ebe0000000000000000000000000000000000000000000000000002c5040e9c20dc00000000000000000000000000000000000000000000000a2de63e4ca6eb0e210000000000000000000000000000000000000000000000021ae69d4a65c7a8e900000000000000000000000000000000000000000000000a005bc241e7d733b30000000000000000000000000000000000000000000000000002aa8c45dd85d4000000000000000000000000000000000000000000000008ff1dbd0f4b9b41c900000000000000000000000000000000000000000000000a46e1fc8b19f097560000000000000000000000000000000000000000000000075006eb06b9214632000000000000000000000000000000000000000000000000000093433350ca3600000000000000000000000000000000000000000000000c8e6e0c4281f705a50000000000000000000000000000000000000000000000043c924cc53c6b0e6b000000000000000000000000000000000000000000000009a11c5c7358610a3300000000000000000000000000000000000000000000000000001c36379475c900f5f028b1ca9b05f4fd64f348afe95c048291c2608303e91a5c888446ce265e275b7caf50609f9aed4ce3fdda4988dff1b9cfa755d22a06ac9bd70fe011311d3021f96a67be4ae494bd7f310637090a697383e48e1cfe83d199fea464581f5b26f5d27b00505d5850189be9bf43d8b14ee58b28a16c87b3bf9d5bd3dae341de20a977a63187eb07fb817d6f32bd7e353ea5f2e4563e05af30e41de8d76caf2a05fb7411e0736765ea872b88f93642e1a0a122c2beda192429812e49797d0d2b0b2d57c7f9a3d89feb36de6e8925068d4cf01c5631bba6ad66d9dfea89eef42507b82d0cdde68023f51cd886423293963e553af03761d6bdd7e82c5f3f70581726faac8f74102eb6129ddf5940c15a1f473a25099ce9aed7c8502f304535e73f02c89d7edbdc59430a931b60bf65dba6ff6c4568fcf3361abd1e9097cc3446930d256472545bebbdf0cd8202744ebee6b32aded93bbab7c8b07493ffe15d753c2e767d9b71f63f422e0d8397639315c74a3f459246674a141133d6068e91cd04236270fbc4be415c1b3fb0340ab79c0aebf168d36a35a77d26f5507817a381e82997d3d4e6bf47cf282de75db04e846b05a1171e1b27b3378df7f46c8a0d5f452ef0980db9a94784dc6878f8768320b7a5f1327d5a0398b9090cde4a0301320d2c66315a1209ffec932d6318d851ff138899b2fc2f20210b9ba6487e63272dc9292a55757e58a10eaf53bb32db4bd956cb6ea7db2f7d3d376ffde54a0c481c8e2b91e9e7d106a81edff57b8a49e69ba1cbce8d8ad7a768f51040cd0760adda1f1b057d97309b56258908e7d0b72e311d563d0f33a89b025e9b29dbae8430af6c1d5dc9be373e533150316757b50b3d7f39cb3fb983eec9d368f3997f12567c3516a615929225c27f1166fe0b8a910ade60dec004d39048fbb5e348d1d7b8f14e167cf8625eabdff7f7262e220e7cad7c3169f11ed47467b15790ad046ae3ea490d5aef76bcb164504f3b903fdde68d660bf30a4a023c0639d3b0df5986a898291ca9055a779c358e4526204e571e880645cb2634ddb604f97aab892dd8b2c61206a8ad742b79fdd1cf600bb1b100e239a25d525acfe6fb97a5c0a13dccd21c3e1be9395c5f4a8ce11c7c9adf0450a3dc13c753c8b0d37894fdb03746c10c45e92b11c11485fd5cca6eef8495c4fe4a8e89af5ce2321dacfb3de59466cdec2510156f196cfcdf53325e7070710c87d4451690eff29e4d32a8e34518657116d7ac1562534b4b1a9267d5813b831d136f643f2511cdf969567bb8528b3b509274e022485fa86f947eb6caddb7d6aa964114d188ffd141b79fde4bec77997e8de82e1a22e50c704d7e5fa81ad6470e6e610932071304bd9547f446e615d36f44ab582ea1f51c35052fc32ef3fd4004dc82b1bf9a39b670cbd3064b42858de6d62c0e202f260e6c01dc5da58f0f5ca5aadf80c9a9810e700b5342d312a883895208ed277759e4d19186345a6d59fff1e1b2df1b9ed20c49e08dba5d430c5c889d232c0dbec12765d1f90c1243fec4c3d67e1d37e80f7783ef9a1828baaceb6f59bad6211ce93b78dc48f06df07f27a2150b2b744cf2a5a06a0df954aaeec4161a424e2eb3a35a6b9f4e985ee41ca8ff60b38bfce07c646fcb9cd13c438c57afced26e28939f478f3c70bb968c92a82a5be84d9453b1121028cc087d1d072e7e6c7c0205fe83b1a01ceba1e0e90bcce21bc3dd3be897d503f20f49b07d818120c8883900aac83216fa6bf485c26476d03e6cad5643b6c5cf217f866e372b1ad25028ac239027d059b6b09551123523e2a5f42d33c4b330bf9b77e09785e6e48fa657361dc82de67104ad93500c9741939e8ba447f6dc1fc4191770b950772b9f1c82772b810282ebc72e0e003f822c1a213b3ce88f42565589de9c8a22cd5e3fbeda3c210803bc3b27db480707058c2df76c782e29c4ec3ed4258affc6790d85b95c0102af61b154df03951ef522e8c2e2d7c0e65d89bbbac33c8119dd9bf436def3cc2b3767985ed2bbab59f583232694b3a362918f9f72a99af2f0f5af2efcc7697c2d2fcaf7859f1a53585929269d86b3c8973039adcfbbc308fee491644c480a7c27dfa9fa5ef3bd09bffee4577c9bbc3690003ea1e3aad93e92fb3d3049d533190deaec9660872e7fcabfcddd95bc009081df6bff849124e9e9df109416fc751b0d2a98b66c8baa3500d82452f976c890f25ae6399064f155ecdb915f4d008b7a002f0bba3996e8fb749742a4b69120e029e62ad24d4f998cb586da076c916cb321f43d09bf02b75a56631867d73c77cdf9da4de3492a4b650d78b39b367dfa510fe1e9c7137dda94b17f265f6938deee104649c6163212b3f9be3a66234369c113a8e3a54aa3505c8bd668b9bb77dd5eec4e994e0024de8f3203d92f6f301e1223b57e5d4a69b68f6d9c57d3fd2dba27ca0bfb145ab0c29922b9a2b129f0e3f71fc82a5b8a4a881948245a642bc118320f72a2723d6a7a9ebb1bfe185529acce021f5fa2445c3744c8ac48355df7a41a8520486e45fce375c3c6fbd3b33bac3c2e5e29bc45ca7fd84f34893fc756f888c9db6e8d1b33d731af38f622c11c5e180602f9f504ce236d5dd7c1068c9f472f64905dde988f8e4ae7f199def886026f21843689e58f2f3dfe48f8f5a637b4a71284fc5706e649278419b7c8e538926409cfcd311cf3c556de1e275dbabc9e67658d311ba3cb5c17542c186a7da2ffd129684a2f9bdc4f2ebead34aa0cc6f94cbab3aabd66c8fd8bf65350be1ef08abc28a673680e3f8c7e41567d1ded0d1c173be5f3f0c7e080b2f1545630ef9644321ed718522ab1f7584f6a4ad83079823dbe5c55b19701cdf61208e783f6516be01412b7a8746a2c86c2cacdb91aa020b8d1db64dbc78c4889165e11b4aaf173202bc28bc70e00bc24bb8cbe3839f97793b66099528624e7d1c7fca00b911b707b19bc81d8401fa754ba2a05bc955437d15102dd440d1969450617d725d1c0baf51b3d8ba9c5943f28453d574f21fc2f9a08f9752aa61f9188a9cd8574593a82ac1794392b927399e2682bd77ca428316d1a2a77fd52d12be4d3f7fd57b9b33a68065b4112e464cbb09ae6d219c9acc029a295cd67fe60517b04596e84de3cbd1602082cab3609a950d6731f100664632ada637279bec7ead801d854a2b92a322b0f0f6f0554856fed457ba9fc25583f8f1ff8d03d55205a052c67db1a02282cc80559294b448cbc117fe65a32ae25d61584c1993159d4c2329f89293162f89ad02d91f842933045e44515de74b0f6da4625efb734d885a51865862a4c2d8104682c6d83cb7999c411b070ea3a5bc939cd54c26d43025654b66025c683116bdf06294b2eb9482a612e3d67a632d391609406b60edbc28b9c01c5d5b43796bcc3d007d36fb02537fcd6d3baa7bc9c3859846fce0d7b30d65860a5d8d27f6d2718ed15850e16cdfce67e1dc1d63b0fc45f1a17a0d9c7db71f689e3766e12786b8c2a2d60dc28eeadd07ade9fbf8447fdddc4f91d05b10d136058ad481f9b4a7e024a17029bad7d3a24366de116e8077da5b101c14da26867735d2524bcb9864778e02dd5dc33744ca7e50e87806b9d773d44dd60cac69135a5d29dcb4b93fe58551024ca2bae5c4c5f6682cb487546e40abe929304b714f50ea72c453cd38dd5e8692c2ae119ad1d9ef3b605e627d35720d26cde98e914c811cc457ae3cd5252b79e25893e23371561e89a731da3343d9ceeafb13593ccbf355ea34eba49f2e63c47289f58bd3e1fdacbb319183e85582f405592fe19656cd0a23a1cfe79d906f80d1efe414b0a22a209c332036c65cc9d46751c183d8618b839afa04eb21499cde40a98b16c72709e82be7312a6085b0563e940fc80e1cc10ad90615a040be766a204743f7867ec779c8a1d0d5fe3de9462f19bfdb3da14dad8768e04f13148df882eec36da9754f273e15636d808ea116523f0d567b5316bd1ebcf6c4de936d03908774b8f1d132154642b87c20ac9cd2de8088f1d0a55de6db45a75543d1adb2806b5975df484e4db990be106045d32642afbd7a492681e8681c84ec41bcadc6e1527f9ce6a7a670a20f260ae45d21aaaa489855dd747e5340f01a956152504a31ebc51009014c5b28e9e845c3a143d89302c81d5f94bd499250d48ddaf47fcec0afa7a61480263c5273b8acc667e784402be671586cfdb7b4db3f8a0f5e1fecd2aefbcf44e5f04a3617598869909c0aceaf0951635951ed4180a0a9b50cca1bb2b5b775f56a5b46a804330edd52fcbdf477485ad2f90e1aa0e3c6febb4f769eb0a4e62b793e1ac9676a35745509b1b9137cadb86a1fe28b0a00b1ede0523ef0229d5521820274634a635d398df9b85908f4ffd8533407e995995fb6a6f4f8bcf15aeacc9028dd5d83f265d2fe95d44afb3baa593f702b1937c3a27a40b6f02d7058a16f98da0cb85c8c6cc07d602960eb28aed5a6b5a8005363783c4c6ddb2010fd07d26daf701ec005d7765e9701cf8617925e4dc28744355b61d1fc2dffd272b74a06f3520227be984cbf13925584c110fd52798ddecd811ade62f107d939c1379e3ebcb8d71f09dfa48f3f2be201f2cefcc38345ae3ced3286cd06dec45e9115b345f3e78a3ee45db7f9170064864d5c09cb0fc3e8f5b7a763846c9b6a87d11798becb574a394dc37646282a7f7c7db3824cda09f9f8a5741937915979ac90b4f7b5de4d1eb4a3b7d6181a242ce8efe9e99513e83b53a66d91e85ac3412a40f2ed30dc99975a2a8aef2f11dcbb15473e41cbbc7bbb7adcfb89f3a9aa708170af029423d5e3e33e5bcd5bbc040ef8843868fc9243d05539ef570dbe1e10aef20fd382c33955f4bb9721ff64b91dc1ccb64d1c39f422bad3029fd747441ead524ce950b1a581caa7944b372cb78c0f9933a173143d3c99f2ef5203bbd71341e1b580d87c30fdf3e28038e52e088ee5d1729da0d34f16b6485002e49708b2af80a0440fec31f89fc13213af1d473835795cc991ac856248d6947c032bea69585093d3901d6393263c2e887bb58cf5f9e8dc4da7a20bca245b87607de9ea0620f096ccbcd54b6f3921fca613160e8e406d7331807a16d33650e418f91a75a1b49155d96ae9ab1de581aebaebcb2cbda8934bab4be6568e7c921e6ee1e9ce8ac5b280bf12e8b4976159f25c05e85fd4f48faa1e5042455da26784f0378846b00b1019ece0c5443a7276dce936601960c620c0f48dfdce3d7158691ade1f4c89e8e16e33e4c84777d3b261850f67e4cafd59caabfaea137826ce0b785a1efe2a1e308bf9cb95df87413d1bed427d514f8d2f1ed5215cc8cc01b960d2fe3466acb780ec253f4c5b3916132fce27c7d53c269e49e0d5f5cfc302c1815c83a39a842a4196b71ab05ffccdc4453eb6121cad50beebc9506f5e6e7a86325c089c1505a5d1565e4e200c0f947704862ea0bbb30d55ef3b0138243795391ea5660caa47102215bb22876b29def7ec75957f10b4173c7f04355665519b869e5d51c8f7e156b05af22ec3dffc3be18ba8bef4b6112006d557dbc866a984f59a6b4850790c67d09dbdd531ecb6baede53b5f37ec5bf114667e1d26ff037959d61dda4206f612f01df169922373268b1e7e6fad457ceafaf8c9c25e62c37454897d9732b6135712bee728c4eebc919211002d9c6f8735af7b8c0a544391a46f28c3a889d0f2e7a104554aca10104e8de7cff3649c20d0b1f54088096a7532b5019e476647fa05e2080741d0dbe713cbcaaa2afa2303255df9d8e0d391a6532b96bc449d704437222d0edd1fc730ed424ad8d4b5a39369538c7d586ba3e59184dd5f2902ed1146e1a01c7b01250ab358965ae82a0f4528fa5ada19814df0d73e4256eb85c0782e527f1f4d0221ad31b598531d3812ddcc1499f25e1501865e17df02e1b8cbc9909234c2802c41b8f3e59bdcaca7bca354ac8a06eaceca80e576276e089cb5f29e61b0d1e6d85b56c11df39d49c0836c0a3db33fd564f9b81b2dab176127f2020091e84be762866d62955eb34adfb2c036dfdc79652bd5168e1eccebbc106c6e0842a6ad0ccc37f360dc838e8fd4104c57d8d74b677ebea4c070a1d5e7a7fc219ad1169ddb19c6d2d9d077f9a31b480259d7c4dfe8e603ec1953634fc56a67550bf2f4c127151b656bd10e7db9c9b4e19e526d93e483d2beb6d6f95d5dd7dc4ef270edb572eadb4f1e9abd0639860e241ae44ee499db89e92a7971670be22941efb0fffb8af56e85dcb0a48c09ae10131bf2a64b0ecbebfc772259c580506db69ac2156fe9c7240f7f66d4714e18bb20c07f44dc0cd84f0d9b1aa509b66ea5e255e0648ed0a2962ede02665952151a54e8cea60f1ce897f00a3abdcd6fae82e74b6212fb5e7fae591d70df03de924b74918ada838ac568031697a44c273484fdd102cdfa567eeb6b89ef0d9fa657e8943950256b1e7053b0824e4bef3666272ac0314f463083e7bb01bf0a666e722b81cb549429070f0cb17f6cdbd435b5a9beb8a2e4d8baeeb9fc7abcf248cd7dc6a01075c51db5fa579aeeab43a582b47399ced1f5044f92a873e6418ba412cfa0a8a2db3314585b220ed790f35ea358f93a7630fa379be8dfc67d5eb98c3aab5c2f533d6750b19ea957acc6bf9e2796920007f14afd5d25310bf4bd31ffb348452c2d021e55f237a212ae30aee3f52d514f4520a9af18e31091497248753927f99eee28859559b268451ff74aba9de02ee6b4322d7570ff915b07f2cb9c60a3b39cd3b93d33e3e05f149db531e7da3325e7bbc1384880b0aae07a0d00123c89a8dee209bd57008f71bd1a6a5778e3dc257782b1f4eb8695e6c34c7d409d48f8dd796db2afc7267b6c14299be5aa8fc680abb4d14671db1342e867fff13ead25dfd0ec4deae57cb9c3dd91d62eb03725d734f841d9749ba103d39a29deea0e29fae6d44193eb8daeb4b06ceb487292619b51b15221ac506544d7bbbb235a8b1e025e6f95a55d4a9da6e2269cd0fb34f9990ebdb13cb608f09dfd348a611efeb3a20ba7d4911414ef238b948281e71b4aee677131b1bd90b800ded9e14d190e183fc69564ae4f8e218035a2ae98adc09e2dd8911283d549ab1fd630223ac9b69f13ebd7edc52739fa0d86ae7bc7edfa3923fb49f01b6905210ff375d48be2446ec8ad19fd201030784ae30ecda07d819104ee91e060649b3d0c9e9e2b984ff05a62d46c6e9bbdff96c343c82a6512f86de130e771a1a878e6a41a6ac0fc0788e6b068a9acdf76a9e550c760f1a7d2054f006a5c71ac65c82a640ecfdbd3853275aa0ad3206b715cc3ba60919d7a2d307a318d84729e3e17fc47339081b3733d471aa0c4a156a7b54b1b9285d483229a32bc6340e223d87b4069b04d0fbbc22d2a54ac37af4289e9138d2648099c28729bf009c2d28faf64c8a8042fffd8bb899554655cd463010c1dabe9bd2d7310594d704f1e10ece778e01fa0d14bc72a9095f381f288abef48d73e1c8f70323019bd3790cf90609f16cf84220713056f45f8783d40e5baad22fef47a7e37b46ff86b16a7050120dd8a0182fefb468bc5f043da2c84b3c3c1233743dc0d3f812011ae063c60c20907d674016e758411738d08cb7b38ec5cf2675460d6e4979a92f1a6b69a6d80f09ae9f8647a6057406542e4b5cad4d751ce4851fe2043791ccf35977c9694b27ad7a862ed2c107a8ccf062000dc496547dc6b4d6f3b0ed91a689dad868bb70009cf51ecd1b12f4f015bf9b58c6a3473e82736e632d1274d46e8fb9aadc92ab01e0c7c4c9d3ffe24d2a3ea98e88d74bca4b8fc56d7d94fab738c555f2cb4ab724de351101cb87df73b68d48f182811d7bd724087eb2d3bf773a92ae7fb3023d05f7d92e780bc1b98e1bcfb57bbed3078e50104684c04a0297a1f054585de92e285d2c460dd30427ebc79eb4e26c0479727e1884df19ef51e10dc42db993de31190d3d6c988c1c3dfa4e0105fddb7ac35563ecc72a4a196e035f91148ec48a4b119a13ca4a98e0f7a165f6547ee9750bef23cebfb0e79c139d02c6565e92464618471f2483429f5093edaca8b65ec2d966a66317e4fdfa8efb3df367f2511fd514737a1a937a481e7d8cc9a03c3a60841a4bf7f9754bf2fbbe5cb6164ab948c00b2e306a251f23b43d7d25e038afd9bb94e90efa334bea6c0c33b9e527b154f0271d9823269f3414e5b7274cc402fcf960867cc389df326a885484a21a1730d30e0d40999fc462c52b6a00344eb43a6e589008ec5dabc7d3b7eadc04cef996180edb9f5dd33c7c3ec5e30aa494b252658237e1f49445b383aa5221481bf279c824a408a0cba2b07ae7260ac3a8b16e1d481d8bf795611beb58cf4994317e4a8a1f43bc309f0b9a6938bec0f485cd9f959630aa5929c9a94e40f0fa18f3ef52d225323ffaecbab738f0a11859f83e7823fab2368e875eb74f7efdccd9d03906d70bc078f6741689487c98d8823c61dfe17c86e49cff25367cce5f92cbc602a350181120e505a5904a146484af259d3cc7407766ef0f9d0ed2e8c8eefb1455df430b15d7a20b2b8ce47711235c340fbc0f7cd120ab132ce965274b695e8384b0af2f74f1a79885f6871a7fa28a0bc7c26363be0e27b50ec7c8fb14c67e80e2b47118205e0e8472e0dbd61b66277d3439fac17951af5ca2d6b5af0d524ccba21b6b2a6051dd94a1b8d7e7704680ddef09a1b1c4419f2a9ec9820da589e9b20a70ab24d89962987ddadaf53f6605cd3710fa08fd60f3daab58c4ebee1ff8f0c7b88616302942ef09f82722e003f1421f3722d0df287cad3f920171162ba91a98d6032d74219ce8c4fe3c7e33b0ffa14c83434d58870d116e6c36559a0c5276bd3ede2f133f4b9b09ef25e76cc4ec02d3a62df652cd9034f372c30b66891e912e1bef1e17181b25d1ba2eacf2827897b7b2d0c5995ca072785082d059c8644febf9771752e9b58e4c98cfc60c257c9111c8b2bf6734c7ac89d2bf8276d6eb14a2be82084e6fdf8ee543c1a3922df2d173bb2d7a7124e55a2d39ea70742821f08726c31b6c80e32786142546b5c35a2a9a2441745ce704f0e55134024b64036bd28f8d2b8119e8dd0e5cb9245d8c9058a66731ae81cdac1c54debe97ba687b26ff00671d6b69bb7f1dd005c20971d3f86f8949ae90483fc8972830abc6481f17f90547263749df4c5def86d1923157f67fa1738bef66ca4fa004e6fc5b4af721837799245cf824a7f53cd08d243f550311fd4a3d959daac4cfc85f32604bb35af71ca004ed6051ffde6dc1b4fe2eba6d82661319e9cd3b855ae418d73cbb37864fe3c10c42f2fc3567df4971fe7a1774e33b8a72c9ff4a7c9c666df10ab3fc86bdd66d2337af6fe5478206b02c5f1709d87b9df0b8fae401bfcd98b33c30e01e93675f1f355765c7361b84de13e9aef9e4fe7aad73a2117c05f9714a0d361e693470c80506dc72a579afc28392a084af4bb406e51d53782e6ca9ced5f2733ad62194041187cf6819f495e3f36941d42ff64e68b479b4c98047b50d14e9d906b8970b9a1a85df2ebcd9f91bd9f9e23d6a103a4398801cf9fd35ffe19b1d22551a09c77e050ac86b551234e153b9b1f200e5e4d128ada63380f68aaa80df274d0bd10de6129ec811001ddfa3cda1b108abf1a10645def9a8f6089074ff283c624782d8e507e2120f8237004abe881653eb8da68e34bd3aeb10e4a19a8fe7684dcd8fb2160e98913519c62998314bbad28005c4a83290a62f9334bd58031fd66bb0db262019b4cd0c0423184d42dd0524ddca8871512144dcffcd066ff6ebf9298ea84e812ae69c3c6009d6328449e1f9eeea8fecb6b9e5c96a196891777b729dbcd009b11dd6c86933cad747984c5842202dacdb14319ca09f16950b4ffd8e8380fcfa9223d511d82ba615fd3506e86326f1cda9dcc1cfd9e6d5f1ecefe79eec809734300c4b915bd8b2f50d9ed1228836ea07a31376c0e1f4bfd053de47bc04acebc1fe0930c6a3476edb691858be52b6de2b01d9e1e4abfbf3d3c5d2684b4bfb7c657919035941a2aae3ad42a9e63465e499c16d2dfab35f248622e7e58e84bb84b71d01e032044b717cab2d569ebdfe7d5097752cba06245ffc7cad1b18d79eb62ba3118c1524d59d9cefeeac71e447831f83c6f290ca10481e6916ae350b88d60f3e2f0058bdb51489572e95e31fd84ca7260abc39f36938b4f87e6d4835f1ad5fb221a652e8262f018bfa3856a8cbd34144e6976d45a453f3ec06fd6e376dc4cee60c6500c3280e6ab4927048494018c8afd9bc6fbdcecff3018ccf7d03b51a6eae14b7f8520c39787f70776235f01793d7e7f745848413062433200ef7f8cefee602784ed98b2c9d273cdecb02ddfa18a8a661d741f3bf87647e3b8d25eba2360217f3b3108225afa2797868ceaaa866d663881a0008e416c5f2f80af7149f93050203465a7c2fbfb1ae9517fbaca023c9862be40b2c84f30b81d237bd7c3798c30891c8ac7a91dc7b0b69ed9e0982acb177108b867adebdf348d894dd510a0b7e1cd010d9147a550c8f12f60691d111628d9182c83ce9536af7ead503f2d86b1b07c40fabfd7c29d47e4f8720b7806cee007281fecdf927c5a8090fe200db873a0142f5b631901ff537e84ad96b1796bb469b1b11cbe42c2610dd49aa74187a4129a1854db8be04d0079d6f88c31d140eed4d5a1139b6cf42a9b792ae577cfc7a2879bf32ae3070900028e0e30c27d2c5d1e44d731bd3d2e6d6135dfcfb18572d107551dbd61f608dff663d88629c18be0406c8ba65f8ce3422d544efd0e1e9000dcdc683eb0eece6d0ba89d46022e636bc1ca13b2918d04900ec63e29f630e7b09d795652cbd6a6479cde8cdeedcac973f5d240002f8f6df8a75ecb9e1c6d2b609938753bf2c7da02deb6f5f3bccce5e9ca2afebf91feaf1265aabdbe2ad99212954377b6f55c3faa8c03088b93f943eb08faff93d5c958f119889f11fd09ff304933fda02023e503321f3311c3248fc12cfcd7e748e21b1002a39c02c01d09209a0ce90d8026f074000e455d119e776d349ceb3da94cf523de5dbcff0f48b0c1a8048441e7c85fcfbc208ede9faedcd17cc3feecdee4c70aa042c6e441b059a14cf0077b5c8565e3c03b57181839f9f7ec98fb603c272189a6ff3a1d3a4ab621ee17eecc490d3d3fe3b69830871d0f9879d3049ba97804e641d42ee31522ecf1859fd0e2feeff6c4515dec6601e327c6ffebc6958cdf901c0ca743f82214679087d9626e792e9ee6cd489e79bb805d2da252d55a99150fe19bfb245a1fe42170dbbb9e93f584301289b415aa57fa49a31d5a2373d55a75df54190b1e216324d0c11ef5e89f61d21a42d90763ef5a89092a10e1acfb7e523d7f3fe9f8efbeee02c371b165637262eed95245171b22f53990e63e5d7277c2d73a4c391af7f2e4e1c18101c45f8cae2939e17c2a4999e38be4e8683139cdd427060e6f476809b670f35b2c56491d7f825eba12d6626da300fd3c80da328f0ea49b767a41a88ab8321c15d42c0af8fadf6438d42bf60c3a2b3adc5871de99e21556ddba86feeeb79168a27ba4c7f46b716afd29c915ece35e192e47cdff65cea5c35dd963a7b9ba5232bfcd3e8240bf1278b36d6aa6dcf916c22e853761e388d3f1a40a433aa34b506bff60e623daf6dee09ccd69d2781bc4d993565b6370e7684d7aaeeb2b1fca601357cfc35f67331a5714552e9eb3dd6fbf2119bbbca64ea76c836020d19ec9a1f48e6e38d20777c1c4cb8531ba222faec847efc90b465e9c34a7001ae1aae1a1b5e613f47a7d2a35f2fcaeb1539fa3306807755ed92968758d518fb4475f80a0a3ca9d6a6143384cc6083082d311bc387ed1b500266acb41b8126dfd9c93e1b1fbc2e1f174be1d7761604d9964bf196b9ee0a8706f863e1822950769d6662051d0bfd2686444c417b5ceea329e2584442c5bb2f75f8e7c180fedc7a3a6dc21014bdfe5ba28573f6415f77fa9325bba1727fa321aef3c424a762f29b3bf455f51babf7c357a4e5ed8cff49b4eafd2746f73fa06b1d5ed4bd15999b64d6f3a7121d69a9229292044de7007ec9f717fdf09dbf3b367ef4a7a1baa46b10b4e16ea903e4edaa9011039efcee40df9f889a16f1d4f4a9a29abd4c5d587197dc1da1d8130ef6d7f55840a3d60e13734ce26b321d00aaab7088ae1779d826e99d92f54b0b80606aa57ab42c90e751dc5b716baff6a81bf135f4250f00aaa0df2c833761247a34288a2ea47bb0bc836a6fef419c37495bec860fb6529465724011b4e58017dd1415346d52b19101d5eab12b19a0e441575d47307cd7dece7b39f36fa26a012112fcc778b55fbd14f16cdbd3acf9a9169d544f15345cb6173467874f657f11a12cb8d9cd9c39420f84a35791fac6511b78d593d42fc2b8f5d872900954a406331151227f3b4ab61468947e78586055c0c09c0b98362664e5e1c8975eeaf02cbe3fa2404ac419609ca705c7393296997f81a9c8423adfd4de864c57a97922159f4fd88748365f7c0d07563e4d453417656c8833e29d6390b86fe4b794250a151fa247a5e1b9c58149170293ce42a65b9cdd8b38d9ce0ca032a2513a10b9aa13bd582849621d7b26e71960d6ab34544826f40f4772edbf65887cf9c90675a02a24cd768068be5feb2b8020cab1f939df742e78b85890a0ed8735180b8dac5a2cb32325a25e5a55c3dccdd36db1e560cf06d8edaff94c8f8793df454115ab6907e1433a2fb8a9710e410cb4ae29b749c5a56812e5d7f5071e3f281e22439f66040be430da6e3d00d79a926e6d938d9e102c8de762f46132cb877c9cd0719ef3124a78afd322abf925c981c80564d0990d6a8ce664e2e17c87b4a0f4d853162214634684c7c01135a65a96a2dd46dce177320faba9c4559efd833f3dd5086ba8112b30bbf4ac3c13f437f5c875730809567be74cf8a9c816f4fd6716a9b82a1c0305ed2a51e4878eac2537a240468d0bca71bb3db3dc44587370c898536d53d80368b46b639edce91842b66e208b95b7592b85d9d72657432787fe6037eb85211c1c8adab3ff2f751ea8fbe3a00085f306569ec17b74af594427ec4560689fb70b4e6ed89f6eb53a9e4512918249b9551e0ae6c9f36ea559e9e512e2689d090a17415450b584e5c7cca50b33fac3b1787034d0208acdad636fc3d616d98f1dda10eb9991d957f037ea435e3319c366ef658c47fba775755c948610e7a384fdb026d34b79b7c65045b283b693ec39a3a461580d5e888e2bac27ea2c619e06650b23ef7817b83eebcdf4c1046def9755d8aae19209f9ab2556c7c1836228399dbd063a417e004937c4e8f1238d59628623dfef098ace7e0e94d254f75cf60370991373e1aaacd754e553d42045943f1356cd63e1f3db516c660ea72208a93925c913478d2a2d2d9e8f1fadaae136ac42187cbeaf41a1fecca67b1d9f8e45b8a21509b73b3cda6895c83a1346812b5ffe2c1a73db6cbccc95409b09f062caa7414f2a7b9182faddea8857099a424d6bff704d75eeb4793e53fd6c6b392efd365d02217bb0a66eb65c8ea113ee6800c30e14140d1db32a753de462a44320d257b6c021f6b2dd25f7507d6320a391d0b0de1d495960f32f421d11d91c7ea58f1838f225e75d81035ec78aae5007a650551a2130d8380a88a42cc5f00243adb65118f62bae9123494c72cc94cd04a9a48393508a4042ff5d4bc0de75cf5827c32aece92426e6a4df489f63ffd696a5e09c4569596d59d11853d305eb433044f7c58f0208708aff7ec7dfba646904fab95e07c593ad93e76728526f8818fd7aa58402a80feee7eb15a2f0b3c4cdb007544c53de0e1f82c11bd9d37ec0647180f05a657c2933fc48c254c582ab7479a9c83bd7c4f8eb0e29efa3ed8c4095fe08a1b447db0a0b2c4929f11cc7d7829662940098723811cecc8af257c9ca591eff5e55f15c1ea2954f52b5693fe6643d48e9f2f23f2143777b0cd7c33e824b1df67327692829046a46469f24a35f7a4a268c8071d3b08d4de93efe35e71b2ae80a054ae6cd0345a6af2d87b666f3f5babd0cd2158f9f1d0454c258d701393f9c43f96de381042f38b49a73be70882662b7070df771bc438452d607e3b501f82092cacd524912d368f485ca72dcc9607f4b1db83a3bd6b6e02d235791457fb5526e68a78d321645077c96714c920ce0f10bfbe8a0fd9da0b9366ee0db2b686811ff10cb381d13e0536e73e9b49c2341023a2ceb8651403207aba6394c9fe86be75b1f3c92342221fafa5b188657ec49279ea611926609c5a0a8b137d58691968eec74595db30a5eab47f49335e4f7b0e1c82e6cff21317280e2cdc9c35446b64b3c2295b7ec14cda916f25e16626a75feeb120e95eebd9754a395186204d03d9241a401df77194d9938769c163b7eda901948cbeecdf3e841abf27b3b8c92903ee36dcc5d060953aff4f2442313a8191adb4c9b374dd9cc5f08e8ba058b5c03dd111f489e461c1723525286162fc0c4846dbc36eac9cb8d32c1a121ebf3d81ccb322277755a2e55761686f53936d703806a52065735bcf568ac9d9adf605be45a56b980d198260445b04842fa9fd7ff6d367f50ed8553de4d1743d992577279a8d76586f479110cf8e5875e0c4682626f6a1e55d9116fcc4bebe725bf5ff24704153d8d7b94", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c03d33039d45432d4999b9964018d8d6552808849c83503f50a70d594c01b441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000c6895e67e144556602cbe13943c8169600000000000000000000000000000000808b3aef60f3e74a629e9331d68a14b202c2cf87674c88d6103108c767ab2ac48e35bee25bf5e92c02761b47af09b71a2c17de150116bdca2a3edbbac4f77678c67c35f056f9e969387b99b5c26da55509a23e762ede2ab19d289955f6fc34a289c4196b6f4fed818bca760158098e851aefe0609e27b1f5e4ba46cd640bb7bbcf5d11024b93b2b80830d9827e59538f1b1e385ae72fb3add3fe838e63d0e080c2409bd2fe5f57f926a01a4e31278a1e0fd8fef8ee3dacae5dd4bd8666f29088e7e2c1680f50451cf263d3e53ba2b8b4135a40b6144087f868125f387412d514b5747bfc6d2fe2bf59b31a94cba4d7b722725d5e5594a87fd3ee32e41daddbf89c75efb45a474670af2162a31702ad09" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000dfa79688268e94f45000000000000000000000000000000000000000000000003b27d8c4969fe006f000000000000000000000000000000000000000000000008596197ae31284d9a00000000000000000000000000000000000000000000000000025071b1b78e460000000000000000000000000000000000000000000000049f29ba73988453810000000000000000000000000000000000000000000000037166cb36624c1ddc000000000000000000000000000000000000000000000007d050eee5ee699fe8000000000000000000000000000000000000000000000000000258bb7095c7a3000000000000000000000000000000000000000000000005704f0d79a04cd41f000000000000000000000000000000000000000000000001cc8608e7634392ef00000000000000000000000000000000000000000000000af88bb11f4b13a3c200000000000000000000000000000000000000000000000000029db74f7fe7cd00000000000000000000000000000000000000000000000f4a5900047ae9ba6400000000000000000000000000000000000000000000000a2f7b686593ac5c9b000000000000000000000000000000000000000000000006e5e184d039f43481000000000000000000000000000000000000000000000000000265f972d900ce28738b09735682ead7a4c64799135201b3899d799236623c3b56cde6eeffc14c0df0717f0a39df3d76c516e8bf543821b9f4ba63b14188d420b92f5ed32350042061c4c31ac25d95dc3913a7a4720dcbd0d2b502eea4245f0e8e0b648eb3664f0ffcf0587456eae3287e653a00e8edaaebecd3e65d8ec98782517ef25ca5f5ed17e994084f3bba80177c052107cd4673933d4d7d498712c3c1a765d4ddd106cf23121b833a7165f5c9a7cbae738a5033ca28022a37b8399326468eccd785a1650e921736cbaea5f086a1fc0b8d34c9b3cf259d4260e3a0fb734e9ba562371b5d22be915dc76a01693899c6a34254a2ee1d2034d4b4afb734de2afae62fe3949727c3b05c1e3ca04b6abb43df9172b580ce32a1ea52c6a13bdabefe41b291b61b0acf905985d7fe37c2a12230fc1fe3c698d1495d937e0315f8ebc4f08b81f07b054fd658b021f59f1486e8105180cf9ea58448782cea621e76ef6e2b502df53c097f54506c667839a3fd6816ce0201339146d1334deb91f8ea6a14405ad6153a182d352d88c84c0f0d3446535350ae13ab7965e65605009887d7909a2fa1d42e2896551a0829706edd47b1b5786e28188506120cab5c15268750c76c2282e001032b6979a88d90b20a1db9d030107ffc0677ebcb6777c7f4ca2ee0510182362e2657252207babe037b83a8d8b3dc52cc67e771540a593d889433e9cb38eec2d71d52810b29a729959f506e4ee054bd6382f9acfcfa7c288777d3a5f4797e9342229726d66b20423a7f33a255cc294690bea014d9afb0331f91401058a271c34d2ab341191877abb7207f8754970389d7dfcfa00ec4ffaaeb0524dd716cfe7cb11e9223897133507d5227ce3ce11671f837ab1ef85d0aa6e308de0ecae5e49f712bf468ea81ea81e5527ee9e0f1a6128b1d1d32c56b82e3da07269e74c46c9978078a4fc2c3ec815158339b79250151302bb388375cdeada8e2669ddf041f19621eabb305f4d3270b826fa6ed2f25db9ffe39fc47e81839abfbecd91abc210133053d60869e919a115fda8d5046e59bfb2f874261d17b698de9e524a7145be6c521e3e2c29389e805e67940c921c0dd97b619b10e3d3e1822e3b2906d448ec85a031c38b5d1f34c2d4b5b7d5b6360c16fc3f097a28fc83c272b1fce5a6398bbe20a3997e59fc3a0e2317de98fdb917166780523993068ec1cdcd514df1fe9083f1991ee90b63c34c18b2639cc3c3df351ba4b8ee53f5663219c845085116bb81c1639cac4dda73a16cc4d7f9e8e607200c85a8e0fad44becf3891ffe93aa770132a39310b85cbbff14f0bce323cc23f445f6a258707355b3779232a642eff4b1a09e4b99928bfd02b699bf248fd01e881d98aef5772a6043cd5619e1a61af8b3303c7e35dd434b2854019587951ffb7e885f616ef360789979c7851ff5bb479421f42bb812a236b6fca6f7ded6823a28d36810b5eac6732fc2ad4410f715951cb2b5e09eda26e7793afd5c7c865de1570c4e72d9a88ce4b31a88264717a070d802a817a31b2c84d4c1f13a918c3a73c88d8a67f6c81fcf41ecd278894e88485c5062a3ef24ec877338c2cbafd1e61205fa95e2b93c59326cbf8f6468453a1e85e054db79eff791b9a1cb823dd9566f06129dce2b1f1c15e9c6d878629d40f61512eced624da055ff54fd71cf3520e5a759b33d31ce3d711c6e1163e3645a725960c4b1b6a5254cb9b135176e02075b45e9e1ea54e21b1a420b1414abc132d02be133cc2dfd9cdbf2a0a44fd11b51779483b4052c658b345b31ba93b918522167102cd4824015fe51e5b84a6fb335ac4842f9ed11d6f5e69b4d8dd8ba64e0f36270f78b42c71621f1501e1ba49fa4e661de16cb8dd3c698093969b8964809106412790264f0ae9b8007b76649a949b69ac1d6ec2a955902ececb7d33336b485a7c2c3c6a1fadf0fd121f79fface6cdf947dae0f776d069829fa900f514ece4141616bbbedf7de53e30cdfab6d5619028b321d44e93184fdf01e7ab3f5a4424386c23b8e5447413e6639c215e2f7bacbbe08458fe90fcff82f4dc73b1b9fa944e91108a5c28ea9df2a9e1b17874a186d1d5301848073af15ed52a4836a71b9576fb284357773419bdaa332b8130670664dea49b96012f02b091e273421d579b8b7c0db2b2715658dfaf910c32aab84a6eb1daa3f8c4c72cd2b43abc44cdb4aa8a0b0dc67c649a6c3b9cdcbea178401c0abcb272d8d5240a492956cdb2e4a3de78841ec42a7c9a5ab3b10d4264cf887b1c81451ebfb5e4277942e5736a7049bc071c168f11d6111ca6731a245c4f6f0aa9acba6d334d552193f8453f06e454c0f1b30e3264e919b2c667507684454b0045f26da17f27caf045c7b4d2f6be92bfa160277b4cf55287f67b9560a68e53fde3b7b85ee9043123cd7039658e7f874c136d1a2c26f2deaf00a930cd611ae4ca28ec30d65792e9493c2175e557b21e31a5102a8f42f94c87e31eb528d7a5fd4ca83ac3a1b62ed65113bf25e5e0a82dd2cae52cba89c170eaf2134fd4b818630ab330f68c040bca7285e619b2d493d713fbfe079f72b0b80fe7f56ea6eb5b20bf7990fa72a7f26191f159a2eb29d01801330d26bad38bdbd921c0e68d44a56c432329d5477f1e52f1d720e9dfb09f24fcc2f610e4303caced66f68ebcb8d876ec6f7c1904ab11800505a020eb41124069deb92d748417971506e406a675a65eebf63796897742b6080c037c097e742a446ed528783529668433efa5bade2e3f9052c11418f54b8bac5ae047f5fd30ba9e031f2bf38e36125c725b69365847ab2eaffb80c917ac7d3a97a47c1a1a9950db209d0326fe9bb980be5ef2bf21a3e85f236f53aa3843581e6e49d3b4451db82ff50218cdf7411b81f746a1086be6f27cffbf9f2352b4fca25777ae421cb673afe74d276522587e772895b53893a247b5bfb427dacfe13c2992700becb2313d3dc00f1876d430708da7298c907dff0b5984eafba5b26cd6fdfd59363b0bc511769da02db19b422e86e8e4003ad6554001fd10fd87d5f32af8a9d13f23bbe12525038a1a7a3867ea9741941c29e3aaa8491855617b16f140ded2b19a0b03cc4a4cd74c20f4111cdff9aaca53255e23c1c6412be63bccc4930ef87a63fb8e59411f56d4214b5fdb1a07fb3d5e91b25b3df5df862ad9cd72d3a24325c122801358aa149d2c03a7e24a687a2135762b2c1108cf1432e73f5ebb8e20a552ad92a91dbb185f11f9ede94d1ecebb9b30bc4c1a7744e2d121a63bb8fb54c2a83dcb8bb61cd6b600571edfb0c5ae31403c4fdebd9bfba727fe0422077b4bc1b660ad5ec5db84b7279d5845420a92bc08b2e5698b81383cc1490904ea5f20406d04b670ac20b49919e8beffaf81535d507d4ca8b08778bb8b791d016fb0c935e0ad7f1c6146cc4a262ce42077ad7d82e2e3848618c2e9514a3422581329e323cfb5e6153a6131990a4326ca63611c14ab69058cdfe7289463baabde69bfb64c895637ad70f47acb101d099896c7ba7b7aaeb2292c376efdfe9278aeabc23363ec0d2c0fad2cf14325801e832e9d2edeffbe63a053f8d6ac11a13118adbe3cb26f66bdc367bdf2a11bf5271e7b3828c52104b02b5f157695823763e7093e3e9e680921cd0c3dce450f4251845969a5c04cfa21f68c982ad2303130bdb22134d6210746c76817e25a249c64fff8b9d2e3651db73d13eea4360f99ebea5b17fd312fee8b1117e4967004fcf53c8939497fd46ee37dc67a58480fa1d8f2cba62cd1a04755796bdb1dbd0625d390458c05bc57aa6cec0ecf86e5b90d7d7484ce043ba332d42367e4b5310c45f065d59ed77cdf230d709944f6dab4c7782c82a2ce64161caa970c2c071c11e59f3300de1c8e44688c10c9214d8ee5d1402f31710c6c033adcef7c521b550cfbf8b66cb58eaf5b4f600593e7a7337c5b37df382e5df9bb7da69acd0935db2f71443d93c858a05143b20f06298edcc053d6f52755f8791f02c80856e1f55b0eab451543b5001697d60235605a12ca6377956f5161eabb574ea08f2741d0501dc63400f0e57099a68241c1d29056336dd70b0ed371da2615573480f10f933212ec6e0b1f25cbfe5ab5958a232a3c6555bc5d9a88ca2a5433eeb71500a178dc1003e570f070517a79e4ad9d72892bf563aebc21f65a15617fba60ab62749c622bb9822e4621ceeae8fc692ec1384c31b3fa7f85ea545106bbd4f0c8b5b03a6a0f64ca4fc38aaf821910592887b951fe39a88fed773f3c32e9d3afc97ee079d406d44e6772130b54178d8e84cf33390e8b880c554746bf355472185cbff9ba4c218facbf53bb5db501c6c583ac7df90901c1a4c7bfad4dc5a1f811986294c3e30b018f42f8d486433a79cebc05ab5a4861a0a19f413346be6108d93bbb7ab24d0f1e374662c55fbd4000b52009915ae41ace2fd2798513822da5c47b69c627522fa60c93b96255feeb8fead0646f6c03c55bdf0ce8c90d8a4ced28275e6005101dff152a15dedc7a5077b8a6a24fb6ba47511dcf942b097d341439041c53dd82030dff114cb5c1acff5f422b9f9157306540025097b5ca9666848657a1bdd8ac035532d8747b5182f12d2d76caa80b6ef5f2c23ed91c230c03f3c38b667fe41f03c5292df7fcc0485405a81a40666c864a1c0bd0f9797d7f00211a2a4d7073be1a8cc3df21420e1ac68e87a2eb75b32473cae6554a8c5118662b250f3c8e00ae2e2973ca4bf0d376bfeeaabf8f7b5c4eb4761a0b51fc018d7a205f02979d60c2027172019511cbaf761d504bfb9dfc8e01e45cd38b4e10103c13b2146174d8fb073f021f5d8fb8369b690f7a5df4044a2acc9e308e6165e6f7f5ea5d0d5f695a201986328ac16e02384a0f49de5929baa1956458ee19a901e8fbe31ecfa803bd2954fe80592a60132ae23e7c8d4d79dca4d60db2dbfe343b01cbab95a814deef0d2f09686caba0a5e8714860f809bff73ea57ed27ff26c921247633ff0f53d010898444c0522085bf95583c6bc1ea11245794f6442c79c3467afd6de0e938b9905fee8793edd702dfee158d66580b78e7124a0aea44968f9ded2dd0500d8263218735ffa49cc44890afd2777727aa4f9895a7039122d6e333b3520dde3f6096c29ec16859e98a504c88d771cc47aec7ff40a9e4f201ca83dfc7574e01261a9ea214114b3db1469a1cf55002bbd38d0ea429b1e24004d7677a57f16b4b0cbc25803108c1d04f5c0c3ef669689f62188b71909f1c11d95d514ca3a78edc8a7976422a8c196a42444ac4076ff06e369f839e38244111f04d4f74cc3ca2d635bc3001c60ee36b09471a5ed2d5db3a067183c50ddd40f2d74469507e949541d101d7e16c41645a3fc37a00b4708cb588611fe15d807d9861ea110e6883f4df31cc8ec278d447019f5d65f772020851c65d6a10ea601c7c4bcf78e58fc905b2c664f0d2c457a65f6424d6739ccaefc361f1e700d1735c61328bc29e75cb0d76b1c013910c82a1c8b34214adff97e9d94943f069509a46742bcd835da11ebb9b440dd3624c70366a5708a3a10b9688401e4250bf48266aa77d0ec6e5403d5db41f718f30f947098f457716160288bdb4721cea1adc69d2745d7aefc57eafbd2e901013b2ccf7ff182845d214e0fbe248a08364934d0ff522a9fd3005df960972ab255052930025839cab3bfbb81d5127ddf6fa01a61a613a6e8f128a85004743292f3cd2c1792d9ba72f886ed7a121297461743136ce348913ca557297019e2a3aec0a51922836c33d87bb152b06b9b9f3bbd144c703eb94cdf020568660d841a255e5d276bde9c1bf661ff908ef89874dde149fc4dceb010e3a6a382e292716cb4b8d916263774fee4ab4ba83a3ebf0b1064ebd75b9ae887644471f86a6bf8221107cc078182f2a821d52e0cfec07bc6657e50526ef9fd0048ec552d0808a742742aa625f90291e0ca4e5a4bd0c2588f4f2072da4cbc09e48791ccc16cea22ca882cd00d0381fb3133577f3d82a0a0bdcb0db0e88fe939063377ee25026eaabd38be1902111b320f710bb3503857b61c13fed36475db6ed1080063626c619d22f9b0932aa34375d85de0439ab7c194f504bc2523abd9627a8eb0c3ddfd751df66f5c95293d54b5e457bb7a9f8362a8f0e2a8acc4586c5f9109067fce81c82242a2eb5f2558d88d1bb575b0651061ce1f9bc8c75a95c5f785ef7933948632c1cb5b43592f88a62189b6ddad9274e56b188dcf13db455a4801c175f2bf802dd46ce5aae405421fc32aa47ef04f6c1ae84584ad7f02e065e6a7cfe8d1854a0f5431c7dadb0437a9c689c31a4cea1d48dc8bc6b635382ab3f7dcab912b6169fa9550900047047ef7b1b8393d511c82c6551d90fecf2662f336597b24113124e3d13b21b27b05f81ed1c2b356defe173c7cb7e4a5d33e4dda4cf7020e7fb85bf2893696a1a3238660d480aafbb0c4271397203e1a80122314f7b9ad807960830b0cacf78e1b1d45f76d3dce3e1db3071e9055eddca41a84da428fd19241adbc656e5cf7255b0cf5ad55a164b6805f639c3f7202975845aaa64ec03b57fa721daf278099b20c04630ee5a1d2cf8aa318da7097dfc780e843e1e2b8d7a550db5cad5d2ad4cfbf2e115682cc648ca983330ffb7283318b6311873be9977637d3acc6b2eb932bd00f387b1c9aed7bf928c15e9af4b7c165e4b59cc38d8e190b8b3a81d1c6d9f83725e0695fb46317bebde0aebedd01b2ed491ce402d501aefc8f4c1c293705be6b000354686877258d5ec4bec33fe416c64ce5925bc9d9a537b439250967bf458702d23d65decbeb6752b51e297d25b2432a6f5b64c0773b205c4e1446b087a44324cce538d0ae9959129c92b2240f863c773941aa0d1c503d20b91f430a467b4f2bbe0a36f9facb42624703f09f4288e1c7b74a9cbc5b2bdaf212a49f219b618c2580560061039f9eb6c52b7688ff624844a0a4acda0f1db128eef1bd0259ac3c176b1db37e377cb9d25a85c75ba3643e894de5bd3e998a4160837cd75f1cdc723038cec04a4853a6ba6c62cd4918f1632ac30de6ed5c0f8b0a68398ee741e60f184ea704315635056068c59f3feb2e28bba86ca05e77abe071bf4d179a0e9e2d0d2bcc51f8249afca59bf04cfb916937a0183917178db300a3d477e6c83f018f0db99b774c888c7019932c91882095d48b2c84a5004642e7ab200a22e90de77515c4e35e9d6ec6d71aba421ab36b30e7328fa15dd936f1c740838181ff349e122ffeb6ee1157415c7f104ac0f38ed2cfad62acb25a58a89eea6a5f66b4bf392520de5b7a89e66f24a828b7141b01fa5c1067327831d040d0e7a07f565528f2ef250121b6f9948bb2f3f2cb4099853f11073ea5173ed10bf2cd22f90108fc7c4c231b03d6f484e3bf256fd3d4f82388d90090764a9b6eaf6cb2cc2806c5fff469294e11a6fafce22457c6817315bc429ad19853b7957af2af6c3389e10c5a105a2e8d982fe73afb4addc3aa9b344fabc343c6438c8a59098f5936add0947217cc2ab165ac1555e0264108944578a3738595d56b7e274644af28a52c992a33209c05dfa9b8864f3155f77a4b18eb1eb793f4721180a337c7fb5a0b499ea96afd5d00044ba5da625f9a36bc38107cbedf12e0a51a8a2f59f2096e0d20e065018da92bc0a7112a7e48a4444a728d936338120a122db00a096cb486e3b67164120ed11205e12b3245fff17ccc63ec28b2e2a1f3d2bc7c8e7403e0c9c5d5a1514a2b900797eb3f3ea1748dfe9e23cad65e839d8e3074d0fd24a1bec13252b57f821f9d068c1cc3c26382875bc0d27da9d3bed3fdfed96ce2b7ddd3446e35d91b31d301164e3aaaaa7534f8ad4eb17b0621e6e5c876d84fa1937a023d2c29e37bb14c9202c71cc7c96f699ecaacae99aa36e15dd8055793bc6276958cbcad551d2448d1199499f2720dc367e892cbec806b3c9459f49f22dfa2b990fc8282a4ce499ba20799ba426355a8ef8dd0fd899bcfc06372d212c6ac1f63c8bd5e6914b18cbcb61704504f2e1e2ed6d86e2da36bbac86aeca425a6720cb4facfb7fd429b85ad57032d86246b281117624b1b92e7da1c0d27bbbff6a32918fc2f5945cd0b4a0ae312ce4799fc5d07c7180005bab2956d23025adfa9e3e37b7a6166084eaec7740518fa072d7ef56b4c1f8aa21142872240e2dbebec78db08384dac3cf7c36628851830f724379349291aedfc0f9cf05c92d4af5be0a50f586929489f98bc520eb906de5f1f41ebe26c58492fcf53552fa0d1df87ab01ecf27efd93652d85bf06180c0bd83850c4ddf904a68bb0f3a24d6703fbb5e7e3e3d58561e1f21783729acc1c3d2d9b96213fba91b08d5a06eca9adb713d1636c1c20ee1c6ff40a786694870e24656dcaccb56da1ff15761c4128774105f4869db44cd54a07414e76bae9ab2bd6f76e121840d8e8f87364deaa979ff118965f2ccb1ef97b3e45187af7eee1034b6d88504c0eb3f27f35f0f36cced25475b2f62df8cceedd3f4a9c7808fe41219bca2c439d0b29739bea7622a1d7c44d8a1de6d59c63b7ab55283f5df05cb70692e71c660ed45d49f1fb0790bdf274bb6cfadb9fe7d0c436f8f784478cc8df189e594ea67b7432713bf22c1199d90e0043695858aa46cccc397e528741c6b52e19c9ff45986020241cec343c8d8f74989c21f2b86e94a704335942c967bc9c032ae6b105131dbd3e550889cd96d3577f80542ab10e53487ebad39ca428ecbb236b4ac29357495fdbcd24b194b37aef7798539cb7407fc2d16cc02647616fda1bcd71bbaf8633ca0fcd9bb267a051933c14313c8a09c665da7ab26b142e9d811d717384396cecc500f378df1db9683fda74d5bc2c4e78fa1a89f7cf4975ad69066156264ceee9b8b8926b5a7105c90889998882ac9420f532112a6f1f2f2b9c023a3ed64af572f19d017aa3ef6afaab087d3bd768155bd3df0fc9f7f822ca88009d54f9ab76967e873ce437bd0f7e3195cb48599df46f54ef9d2e424a93f59213e02a423870816fe39839111cf60a4d2ec4acd8e43863cecead8f660361b8a903bb39e0d785dcdfdbe89b85c713b3355eee749fb6027ef71e9984d38806510f18a4b230924b0b848a291f8e62922077c1034005c5ee611f550a746bba75289e04ea00ca60efb7b8587281ce5223eaaf8a07771df77a7dfee0d61b4d1e01602517cdae4f6c4ad7205b5c2d781c36bb7f9c36d3a1db2c4eec78e001dc931594700617d097a075cf9b4e688aff67aeef2b5befdca4f21e388b8c655c2b55c24e511251c2d400d40a3946224c052f1ce75a4442306569e701bcc086439a62f9e04c0758fd7bda12e1ce40b209d30d67f2f003fea544afaa1d0420f6608116269508081983a5d8a574b216c19749a1472029898399220ee4a6506f95e7c5787f5b9925e200ee2c2cf08cf40b446659aa9b72d867e89de012b1336c390f8884c2087e09f19183af33cab63051be64938e007258d7bc38158dc0e80b4f2b76d13813ba2926c0c2da99395ffbf9e8cd65c95721cffa67de1b3aacb1e40f9a75ae46d9a11e06d148dfe7adc51debf3a5ed2958ac53f09e30b368648762c53d629ea154aa10cfdd10faa8de5faa738ed3d7bded2740c0187ffd6fb2f51ae27931572d853d27b75276de25a8deb23b075f1581f71d8b45f9525f54c924a3057476123f900f1f8fc748b50883c13789486e4a0750c4c2b32d747542761d6b41b5afdb17086b17b7f3f2125e5df91bd8ff04ec54b3b732214ab81bf6e21560db4780f1f0ce7329d6540a217743efe23cd1c6694b52623718e1478973ba5f76afd37d376b8930082c5adf2d53ad44dd629106b3c3b3ef24829f1570d6fe0e6bc673a33d3f233406fbe881c13361f5495c94f2d43887657f474a5ca6ebdad16ee92ebf3755c619252bb90cee7c61ef1b3729012c54101ff03af47b6444e81ace561d759d309b050a82f6e71cfd7854a68ef7452aa7e48f7c5ae0d48b5f2d54902687a8f0bb78852bbcb6b17cc2f8cac0ec1526e2a60015d3efd9a0426d82aeee4260b71813bde80392ede8d86bf0d16af0977963ce5a573f7d561fcd20ab05e2db8ea7ccb334ab0307670beb2978093fdf7001199dd0d72b0b7752ecfb91da30847c8243072a7c2f199d811b6a6c1635c6ec835b05fd77235cb3d8451194f699293e70133d756f2c9881849ce8eae584597a83c53a47450a578d88f75f6982d17ddea04e9b9ed5095939009049cfc7cd6cf9f412a3cde99720adb91ee077da2aeafbc7a033044a0b672f1ad786fbd543528c38703fb4a37c1db9a2674feabf00ff33a5ceb01bab1aa064e846cd5d748df1714875b6a62c9dfe562519ab164540c0d7530589c288064e7b496b7184387d1d4686c18f22110364c55e53f3a70d016832be570218a012f6b252d06f73d254937d9062a293346adc20823419633ffed17822bc3259dc061a1a8863d10da15f11186f71f04b20e0d152385bc108ff740a68bf824f890f05b422bc8f53d3255aff1e028a79f6da0e0e24e931497d03ca96b04e687a6f6300ef2bf5af84353c1e1a3aec79fe45be15ff7c3791aa7dbb919da84d1106515f2d5ecbc9c4928ef292fc4de434d4b92ac996d21ebf2d7d0b9d99a295622cf2ed2ef5cbf883993dd846bae8f4fdbae671e7338c6e1743fb26ecfd0e40e409c60b03445ba5b1bad93b6d8fd88b0dfb82ea5a9a01db23cf5f74ec68b4ea42a309c624208abd552ce7d9b316865cbfad7d0c232c8f3589cc58f93168ad5fef7c3843002e357ed1e5708133f433b4e0976a4b1d8f75d99556678833e17699e399f9970d0053375c14a0ab5cd8135fe265e537ae1d98e32f64258458b7ca404128db1e2c187a91f43be9f07bbdf161b002391f1b31d31a2c8f62d7dab2f8d7c4e618a71a943d0f30fbce765f0a81a0b86f000a9edca80be34d9fb22fd5959a52dff8b214d45dd690880833365f782eaa2bbdba3cc6758d345dc129399b5ad29e5d8b1f25d06a5eade8a0ef42d3759d662b6c3b27d9ca7f5804c2b3e92f1cbf128efb05252410af5cc57c287a8f8c6c3e7e83ac898b59003c3c39da20e238659b09f1df157216dafe77fb079590574e4b4123bf304f18960ac407b2405de4825cc00c35061d04805f2451cfe3e026bb617a8e284ae1bb816526a9c506a33186bdfacbd016102199b0ef23d253849057f884b691e22de105d33dd87777305d5ea3c564ff00af3dfa4db7f4b2aa305bda33a2476a6b05cce69d61d9f75f986dfc4527af3f19102cb5b69b2842d6d7e9e90e8d575adbc16ad8a3003209e911b2a11d4e200225ec90847799d4821848766737493f708ece7a692e4885692d011ceade273fc4041c5d41854376102a27aceaa0aaa4c3257eb59982599d817fb2605623931306009cb304bbce9135174946a02bb039b7995536a8c4dbb218259ddb2c1c5b9dd411f093e4e518d590f06f8c106fa0f4df4b40f10db27b63d53eb4decb2481d15a1b1f9a4dcf66168ae5f6d7c79354ec366e79b5875482aed5d9a686fdb62b17630dc56a8b87bc1dcb6329bf2d19a56110dcc82f939acb1a6e70e994bb94ce7ff31a3b1db0cf696f5c842eabdc0a6e9d5372ecb94426fc8faef668ab3d9576705e304a945cf241ce3038a11c6f5622a3422ca25d6d0040b99e5cc4e4b39843b7d72534843b4222447511a5174d7c860e10896b36b505a0e16e37cdf8ac5691664e1e5ea49b6f6f35384f56d9faa8b6aa800fd2444e741c31814b3bb2c8c6cdf828187c46187808eac8eed732b528bd82ac8f43e6db3dcd19ed12205172f578245a1589aff49acc083e14f1caa589eabd4d490f6940acbabb055c05319dcded45bf21f0d43708de5ca2383859008ca641deed357917f5ec7ba58101d82a5cc8a6f524f19284b599173697a880f26ba158d39dde2cf107c3dec0a9723aa76b6bad28134544a94eff974c94c041ba956834fb7b8a20cb3f9594b54e896704c00828401eb747c909f8e2fd22eab075a9078c84b13b6bbac635ae9abaadb151c6e827b606742f6ff2939ada26729759830191df4708924fea7ebfebcff8f927d22df05724b550de94617b485e6049826fe78c7dcef0a4b2d23fcfd60d744b89dd19180f1b06d00b6b4c79b71bd446267e49ceaf11951b2d9082b09d0e3489fe070a744a0e64fa7d24d7703e2e82a7f26381ca659b1f66255b7efd0df075b6ba4de7a085030738dcb43875953999c2f7d5392df24b864833ccc9bca50062a633f8e7b8150bb74b7a7c537b12c72b109f8c212bdc542940fcbe580040f301baf107ae48ae17da9f15e0d3439be0bd2a45788973bf8745d03428478327de779fb78c055f53138a8e8df3e15405872ddae7b1ce4515871a20b0787503e861c20abb64c517e0042a2680e7a04527301326182626e560ba34cbcd567a6fbb797077ae12ee618312c49cbfef179c6440930137664d6cc87902a61de833c0aed0a616790b8a9ff71c492e8944bc3f60fe2de37788fef01f939351f4b1fc27098590ebadd75746f60b012641c5ef934924a686fb002c329d97277967b2da3706e342c7418340cef72d0392b4fae0d4ac069893efedb6739f3bf6c4ccc98c201a908dd8f2c46ec94720c00cce419319dfc91c3b842b3e8591e284dca480d39268d31117cdd8efc4e520f283358f61913978ac3430c515e7517268d57226dfab2e09755fd0fbb8654723c336ff3932cd6cd57377fc84ad99e65e92e3e62a10cd8cb84d1eabe63874b70602ec92c43565c29787cfbf0eb1066ce59023d4ae6b066217bdc050dfad86e31c4e357566f631a0ec0d3fa61c328756f7e8e9d5b3b7c8f86cbc08daabc2e8f31bf8eb84ff8a4a6db3643f1495b21c73cad93e3e2fd220aaf09a29cf4c9c5864053a1331b441aa1c585625e656e3e58df740cf3d457c40b0429e10e67b7544310643409645badd52389b0023568f354686018d5cc617ca0be53ea64144ab52b5109a28e74e789766193593c8bad70d10b403c06dfd7ca3b97079f9a4974f563a19019c8c895f9372f50226c3b0976ba2dcba703af39b1c50c7a46b720ba7d6491ee9143c7b653930901b4617eac146fee04757443f36c7156af58c8bb33670bf2928276bea49e7849cc27cafdf183902bfbed16820c68c2238e877290b90a118068947b5a8977360e5a952e0036257dee5e0443433bc39b931f3a78a42a124210a51c58cfd80c56b67a52a3238b4a47524f9e269369e91ec03c1b7ec246c04420d0035d930d1a4936dae183715bee51356873764905f7e7ca33c3f31a56996e22caba046602b6b24130d134eccc1cfaa4eeb4a5eba6fa0cbc022057243d8d478035a50d38b9c15b98af87ef36ccfcb1a41265398b117976d06a7b604b0945f6724c34befafe430b6e687635eb05e98b27b9e57fc05ceac658bbf43be2b055ceb12385938b72f3e4e0ec92cbec13b6b816fa60b5404b418c33fe661142a616b6e24e8f9c3bb35d37e457d1dac5b9c74f471beaf7f8f4d26f608f520ccf0e113801460fb5f0d9b7c6ec1f81756e4486d34752010a0dfd19ff23b9bab9cdcfb1df1251615cb508e4922ecfab788decf45b7e877c73e46391f8e7368dbc5f351ebce144bd31203ce49b741425627c097ec248008f704ab6321f4c59e6e6c1adcf21d255b9c0abc5e5d1e913335b2f5c18fed758628396b06073450bdc8e51b9ac17a15988467eb7f3ec60395c779f28a2a1d9998f24515ca5b47eb5cc29f875c9d1a1165b3fa07fd3b99b18269fa72901035818a9bd02031ad308ff76ede118c97350024db3130d1c0b83bc7b65c3bb1f886c3996c4cd8939139aeb8e749c34ff131274570245f1e762ee28f9876c9e240e56dbafeda6fe7047f8f56148b0440f23f0d011a69b14d6dc35825881a3eba8c695d89526348a2e783135364552adcb2de2bd4bb42f48491930f07c20e89c9f87dc463d93e72a7162f518cab3474af3cd60888cd45bad31620195330ad1fa68c7b800b83761b351b440ee1542ebddff7fc14b018c913564c935e83398d0e28e5cbfd160707ca15b6e4efde70def302103524f2d9162a684c86e6dd17b74948e34b7523839b5557457dae8b84d9679010b82b4f9b6ccffc1777904eda4e4583e195476f62b6b2fc96fa400281d935327130216f637efce4bd7967925161f603ef63854cd24c1637de8a8fb2fcc7031bfba6108270c3dcf4e240a99c15115a88522efafbcb2905c304d35cc0db64799797a10e49bd25c35c762064bf13db5ed47baaf7dd7c0f4a4e768926eb2b5f9f2b7e9b0780258805e32665ef178dd88e89fbeb7c2dbdc7f4c7672d6cfa8a46075a304407aba54c51707cf93558bf6f2bc15e138ce94d38ef015c8abdb37dc627159b601389b40547fdd18c07caa2e8c156ac50719ee2e1e57d111bd79ae6d03a1297a40c36bfcb7110608d12d503545f26013b67f6883ade09afc1a9ec2287c5e0b4fc18961675c195b7025239a4f728b6354262a3b333a9f49a39f5f740652ce6fb271e03230bc5559de174f64dd0d90f02b5f2a2eaf004278215a9d856042f48cb3e2028a458428b511f716a00e9432a28ba15a6eb8cfd6a26c33641dcf788c1f6a71cfdd7f6043c8cf84ada544f719aad323ec1d2ee9f5a4888114e008cf4c467cd2b509709b141b7d822abeb2f1122d0735485f29454e0077ef21016ddcd68fbe9", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000a6a7b38193b9791f900000000000000000000000000000000000000000000000938adfed18caae40000000000000000000000000000000000000000000000000e21f5e071314e88420000000000000000000000000000000000000000000000000001dcc3d8161f47000000000000000000000000000000000000000000000002e46c7ed7ae9e45950000000000000000000000000000000000000000000000006b452c19c9c3a95200000000000000000000000000000000000000000000000f937207f5e9d7f27200000000000000000000000000000000000000000000000000011234ce13989700000000000000000000000000000000000000000000000bc917c80a1df8a9de000000000000000000000000000000000000000000000004158d1dc6c5fe0793000000000000000000000000000000000000000000000004df57309713331b9200000000000000000000000000000000000000000000000000007698a06035d700000000000000000000000000000000000000000000000b5363c615bf3049790000000000000000000000000000000000000000000000084836ce0c2626f54700000000000000000000000000000000000000000000000de7f5f0f37ffeb22c0000000000000000000000000000000000000000000000000001bca3c66a8927213f200493e130f2f241df6f50e717977080ee976b404ea52ce9a1739a6538942186c4ce5d408bc4469b54741acb17f81532bb104e167f13788ca258657e19ad18bdeaeabf611459f9516a80aa25dae22a716359feb8721e6f73ce82c65ed90504097a64060c9b98a318d0db5087034f8fb8be44fa54632799b74281b02fa3201b5874dca01aa6c15157bbcebfe698062bc743018cbf4c1838fd88dbc7725f1e2cc9baf93dc954f7630b60d425d36ef81eb1be7d8436127e0444eeea525630a31058031e3a95f311de89e96eed8d8edd125cde39fe180ff58ece8d79e1d4b19f2d6403f1677a3e0f98f7d8669a44ee9d1c5227bc6dc8f7fab18b5ea38a23a8a909d6b647f178c12bf5765e4f3fd9e9353f1578914f6289ab3626968195e2204a0b75d68cafcb752224084dafa2b4ac94248907a3cf380066780a921c968cac8c1745343af4f66eb1673679b9c00300c26624904c97f542932abd632e9d9c20b613f65677439219bc9826056b26cd285c1f6ad8cf48940c417ae82c3b6dda3ef601d957cb411fdb0e5a236ac0758e0f106e5c2aea4d997e4e082051f36f37a07e193e06fcb0040e75764cfe7d7b19379e865f44cde8c4021fe7a2cecf7276e482125b04ce7829f541ae0f786317799e17a5d19b18c35032546d862d824f01aa2b2ec87c35f2af31bba08cf65076c41c5b2a532fce570414bfc6619bde14f1f90519c766248124f4e391908fe40e4f4c2e47d959872db00ccc6c66104be6e3a4a01509488b91bdae4548075814bb3cda9893c4acada580aa2c29959515a57de7a31cc4f2c4335d29fdf50154c3993f6ae9031d06f0541f0e718afaf53d83631efd232f3df1898a0799134d1f263b0bfd6dccf19c72088d8811c5e361ba2efb54be181628501e2f6edc49dd9c2140408a2fa36d107456bc9ca11d66c1e426b5c3300cd87deaf468a61d8cd99c96247bf074714476fdffb493aaaf976d4dd235e99b154e79b2ff5e50ab7f8c6ed0796649601b9c5b5efbb5fc5f5b4f216469ba83161e04612689209e87667d0a819663d69a261da2a789ec0318543355b471178ee51394b1536f2652a7fd5ae364f01c53350a79a0a2c030636f3c2b0b1a4f11f8f70b7dd2a2ab687739334dfa9d83fc290414ae7a1369b7b416834d3971e6e4d10c1e739ee4f5603547ba8ba72746f349ac646d2ab09e4faa5ab7e68fd55609c6fe055052cb8a99d490c94cdd0e20fda8125a7fc867867440b977632d440bc72ddf2a79774bb4346a8defd31709e0d3be983bce16ec2f1b6eb0d064df61d33fe4eb0c0a9a31f221442947a6eb27a710db271537ecb73fcaa4ddc4435b85207362ff13ebd5a7cea692e2c11170b8f0e19c3996c9d4fe03f579bee10705ba2cb6a64120e3ec3fd8a3623c614a7233e19fd586b3dcddb5ed038a7b87085bc0d01f35ac0a910d04789ce430f092f249b8a95e3f3e9e57d464b7e39b11b0b2ce0511a53f06732a3e7d8ef4a98f1c0197fe86514ff68a832af68a50812a33c5b1b3d7357f2f0fcac439af16b2c3c51ccc318db8bfedef50919ca1df0ecef4cc3a1295d63a16e24b4427e5397d2aadf907e4021dc59573232a6851a4772bd0d7a9511e5e940c45c13faf58cea6eeddbb0a5bab2b269c8d205cd66a41192cb1a02c691451c60cd43ed39782c235f5b7222db00e52277c1bde022d68a3dbef6836899945390002967f653a03db955a5c8ce104f9bded599366c20d9e4e10815d1676c7de75591876c3b768ca0c50d93bbf03fbebd73298b550e6731cc9df74ce6b043d391fe501dcac5bfba80aea4039869994a026ae689f9fd7bbc45b9da35927b07a1400b8050faed5f52a92daf28e2f7a1e113694c5751fa2819d425c7038381219f561d31ab37ed6c11585362715d889de16fa9c110bafe87a89fb3437d64c75602257e3111a8aae71085083c00061af68a50a444442c811ca0702b1ba59efa80707a12e0574243f7c35ad11d0499bf94e73ddb1a0551fd5dc7137b31c2cb4ebc6845b5c11c03ef720b358f0b3417c3073e3d486c03aebb7b020db1ea2ebd23e08aa22bd2657df69bdd20d3023feef2bd7b0efde8763523aae1b31d1bebc86a1577122722af7b9eb04d62bb5d2dd340cb78eaedc412be64449d0314cfaf30771d59b4913156f4b94b14bf9ce19924e16e516ff1bc66e6955224645525d4eb36650422744014aeed42b2f48def893f4b59685a7a00fd42ae1856463b2ee65aa8f09c21def2f6d4a920159b6487b8928519e70a5adaf40e615c04e7b549c276109ed3888e808c5f4a847d257ea6478a544aab60e354c2cd9251e4456ba225db340d3c35b841ab4876ba9361592e0e2f351f1cef7a81e5875c24f4deb7bd8720b702b4d02941825cdc9b2fd2f8aa1e13b760f9e5f6b87dc45dd74defd1cb013a8894bfd211818a2926b858bb7f334bded04473e5c398fe186bbfa744b7c667066c72db7d9a32d8649606e140be47dbd386381b1fbb1a6238a8c38d9346a624d27d057ca8ef71e68e91fdcd8345fca7dfa89faeebce29c454669b769baaaba0fc712dc541ef8280fe2e79458e28de1f0312d7a3dde0572fe68bbf86f046faa8c2e07927b080f28ec7285880a3c089913308ed80d9c983352b34317079dc36f024c88cbe698f1199da4d50f7601e1a92ce7e958b907c3d08ec1c966d10f838864b06cb903a26f183dff4905cb4bd6861c48fc2e538b3fa22385197567809a7167ec25251e71811b35520a843903bea42f31b157ee343aa5335adec9bcd3120a6d1e4edb158539292537016521f1e1719774bc3472bf8cea73aec615aac019b47e7d879642f462263b44a7a876820f0117f968f8b0bcd1478699159b190360eb331181a2c04be40bb9562ccf1693476d7beea398beed58780d39123f31c1c401b3e59502899dcb070a2498fdee2da8d6266a91ed4ff61d016eaac361e643de776750e0b5e0675313820988967945cf62b69b46bb5aa6914de5556c053dd102f5065ef07c881fcc2858d7069c1c6f7915d99118c1d498f3232b2fbe53f19ae68faf49837c761efd1110cb2769cf48c492062503b64b03bb339905c4871ba162089ecb07e88f922b23a72ef676abd989368ca5ca6efaca4a3546caffe57f71aa15550539f41efe8523d17d0a9e67ea0a1f0e79adda4f7729011eeba0d13f56e2e03db3aa4ea0f1b62ac55a22371b0798281faa7e0ebffa3f57ef9db292b0a30d64528ff64c693dda2a3524a0f84f2bb33871e969321e34c0af763e3076e7f460e92e6cc346eb967e087fe895a6acbbca4f7dd6b474ce3421a5c24f07129f17b389059945b911b318113acae2204d6152a672ffe6be7699486fb5feb674a9fa1d63454eb801e0b0071570f519abe7972bf439e8cf28cce87ba571e359329415b61dfd9605d2e5ee440603623c89bda7c629b4a2dba72f964bf3fb0a45d364198059f2bd2b10ffa4bb0a422889998c100754be16415e5a16450ec06bad480226610e78379eb5be4c2f04a91545a58c5916584ce39798d9f1dcf7742481e3fc90226b8a508e45a3179b207ca949a9e68369a4e7a0e259d4706e083f51f4172b24d00940a5e40c5041721c4e2ba1ee11f1bcf9c2ba0876dcd5bc3b74e779fe4fb8318c7406d47ba5b7da0d76ef4979ba783d83ebf8c7d834f6b0e244320263afd46395579b9a73d00a400646144e651a725f8d554dd3612862c70c98fac4a6dc4e7648ae1c99498221430bb83c7a9e1667315b09ff19d292693ca49c81463918d0c8f89e56e7f1cb588f12100b03aae55f7fbf686c6d8fcdfd2ec95fbde59c33ef6e9162c39088034e3427bfb892d72474b03a197db7242e68fce96b631564d2f94eed762c1c722e38040e22909f91577a6ec1e2c330e5a47c8bb5e6ef018f1a83388c73eb754ff32cfb0c4d38ba8b27885ddadf3f520c796d6fc499fcbc2a3c5a83b97cb77a5174d60d2c1234826405656d94521ee61b88a233af7754bd82be24ee44bef1648438d4fe075b2767664640965dd21e2f989bbf4f2f69c5b1d0d1ca61c90f1ccf62a2f766251a5ee4b01447113c913372d0d49d6415b7a479dc7ef064c184b9140d8bee6a0c1089dd993d092a9e654da9b9d13748d9ff552c1d284a9543151223275818ac1bd3d204262544f9b536b40ac64089e0ffe99370db2f5a5303b14f7f1b43feb901c4dbe086dbbdb2f83982b5a415dbd75c8eb212760574f15b9065a39654e17829f0ed29bce220f0eb2ba77754697a8babd74c5dc104896f4bef310d99a77532076a118a429672a66d8dd32e72a259623f4a330291c23a0b39b4376abd57c6b92ef5e2faffdcb6fdf0419185e0528d35366a1b865dc91dc4a1e228379b219eb52a47d3160a98e6bef560957039f8687bff28bbd1654b0e1a2290f335c53f0e0016dcb8f0a0c2e0d7bbccd7415da0fd8725055b26393b87f071918eda8414cbfc13d7d1c5beffe174876dd3ddae96fa6cdf87acad3ba1709dea8c80f4c7d2e4bf0e894e78471fa89f727f99c2073b57ad0ab2b23ab429995f9eb947fdb176e28c2ff432eba5830407d95888ac9843beee5f7c224ecc1a40afaf7534b5c3010aa00d026e74c47892b4c803ccba015b1341b380f9b441eb79f26c0b1c8447d23f912dfb9d2fe28a5cefa496ff3dbde6fc9fa6767339c8035ca97086d6aadb88b0dc1321688e887d98b25cd01819655e9b1432200c665a0a21d40e7e0244f1c373a202aecf5ee088442b4fd2c5313001a02f584d75eb7b6e52776bcfe474f016bd692a2062da16361e2626c90db4aee731753bac3d68a2963f02fab8f43d4fbf52461e1737999975f52f29533837f361e89088c3be55ca975278143f7bc986da19f71c483e99e599ac5ec9c532fbd2c9dab0e088b6978ed7e65dc278a736e5d03792040ddeb18cea5bb841cca78a2f449e0b360ab2ce1f1dd4c8cb39ea12679615111308ccef9632654f64e19d97906c95bebb106b2700a865a5165b9b8ff470a3b72fb6a7f3a91f58d47828e155cc94dcdd10ad68ba207859b696ccd40783fd81f814647a611ec4c8370bf8c01b1ecc618a945f5432f2981b1c4b34c75077d83d252d91e6be4bafe7db360df60b17cdc52871f581610fd0a35442b466230b85830d294f38eee8d3f464f1191bf6f3697840817fab5682593db32bd18c3e35c279a623cd32c6a3098f4ebd0c1b8944809175090b273ca8fcfc6b2381835cfef50ad7135204c539b5ef18e1256f2107d81daa0665d4e3bf06f0ecfc0a1cf514d0aab105a9eb9f695af73b3ba0df3cb2d1c46b7c02bf108567e6d01cf49e6d767d078d0f7eb40d460e34d78692e0114c3f260886fcc7e00c8b3c7008ff429d1a53aa9c2e2a41d1cb34e154057c4686b4ad1d704d5fdca331c9f8168d21ee75a74a465224f3327845653d52575e720a797cda03fef68668ce7623ef7cc9034bee94b8b0181f2b11ce4e8710e7497246d272de79509f4e2fe6c8b42422f866c5c69f6fd41d41c9c326912a2fc919382d4e3d414ef9fc9ddf546ab52c8d93afc585b420390737f26228865603a7ae33fc8e19c944994028eb2a20b57bfe1a2f15f0f372fd14ef2c297e78695908b0d6cedf144040f14908d59b7ffa58b1b1930bd65270942bd8043e38037bb5213ccccb00db3df8a661e6fd31d165ae465810350f8182320df72bd876d111321de34fe704b96862982f670d697336dcd782eee7b90ebac50dd31b62a91af03d377532a157b4262a7d1cba57fa045d75bda66dfc194c0b8d09ef91d887469dafcae262b0ce0c2d8876e189885ee793cc622d42f5719247341ca89e1cdd4026584e5087558fa70f750cb3e58f15f4655cb2aa5c2072ab82f11dd0b47a22d769197a373b5a799afb067f275a5477a2592432165fbe5e5fe93416f0816a98599b5cdb93b09175bb78f688bfa15eef313efb9d49047188736c9f096d86d047d951f18de0d5bb2b435d9cfaa88521fa55553ac1171aa3676e41ef1387921e9df4c7c6e996e54033a9115e1159e9cedc6121ec9e81a1f516dcde852f17605c68e6a41ce312832b731dee504377e669986a4438985af9523357a9f41ab6b9d08bd5e05aa971a009802ce32e31c29228a1f314a2da664067dae295662923d7715c95afb00e860d12d7b168ae050cf29e4bf1ca4fd61b9129794383fc25db22ad3229fe1d00f4761e37e897caa298944185e893a6bceeb54e22b467102f2d8e3e1b2aa2658687b4f52d165f059f4053284f6f3797ed016a1cd51d6ccd1920f19badf516351bb00b5ac3bf3f00da9600778734dfe59ff118b80c46662518e010c4e1220cc9a38032e0cfabd5a5a5da75a1a827179c567443a9f95c14710c03c74cfaf3ba71459a2e6c84aaa0c8dee4d69514795029ea15228a4696961201437035923818557408cdb632f2206a5d9b67d82fb00e9c7c62a3a47c330f5f0875884e7471a7c2cf9b2ed4d9573c90e0ddd52b583e9ef1b23884e89a0c0c801599f00ff79b96f561378e455584cde3a3f5a4c84a86cb62c0998f4f6beba9ff1d9cc5d092a623abf9cd026ca12b2c816c0a63ba7666422d2609f8896501f94116a4ffe86b44b683ba8071cdd9b7db1f7aa2a76e30c1607e8a6aa03a5384ed982b6b6e809ead534b62fd2963a7027800b08e53c94f16a1b00b753322db877ab20fd39eb60b9280409a4e963c6e5ef6f66e7f8713a7690762ad71158a9c3698d42fce32038142ee603c46b0d6510f279e56c2b05d7096c2416cc3dfba58fdc0c50a8b5d89048bef8ad00d7b6fc744234baefdbc29faf5e4694f0d0bc87ea2397a0de33752094611077ec049e3deae0062a3d54d9c55d2986f356ee54d7ec2645c18012c3dfccee4fed0bbf219d4cdd93b94c2f1dcdd964a7ccba4954033a07815248455ea2bc5b096f90cb81d4b534a82a8c55f8df025430884c395c2992128871e87c67a24158e7d9f2e700b685d9450e5084256931384cb517ce80ecbd955671b13c2855124079a3f8a423b00e0512ba3ee812ac85cc596ff81ccfa7c31dea015cfeea3704b8289741c066bf18d60a8f6e453921513d10a7494b7a8ed8bf4430e906fe2dca5b1f2daf50b5151e78872f7f971fde706bfc267af4d6c71c43c461b331c16f81c053dfc33bbe0973f91e979d96622bb2805eca8bd1ca1c86a7e4f2222e04ad38f0cee9892e7f9aec41ea9fa52334b246d476203f7c2d5f6575b7d126f845c8396208049ac014e067a49c7da865ae3e17c4b4630e63630abc4958e1924a21d32c26166f01ae2b8da72d7a56e1afa6621287663eb5d2983187964f81753ae5213daad6800140e34f3af2d7e227cd2c5bd9d4758586f6a4d60cd0cc70034d64e1fb4d2ab6ed9be9b2810571f2189149d9dfd08f1f8abe05475d3f445230413f3c2d8d7a32ff4c27a59f3922a9523e81ceb9a2b0a35482808b08de05f2686e7dd5fd0b5cf19516cb0558a76d4d97770e28404a2e78dcb63f3b96eb19f1bbf291e88a6145eb27e6fa5ed3609e829094f9cf937c58dd2bbeac045e27afa19407b425abe489f434d671692197d5cf72c07c8e2ac447b2db81b8e0e248d621f0521aeedf472184eff3527a64c13f0c601b3ec2048af2672255c14010fb1430fdd8b36f426c6469ce3c6b0be2cac2ae4ceea8baadd17bd0900a75114a14a370e499163de5a0e817ec703ec5b636140892fa8203d20c032cbd574fc34d2385320f8ef889048179132035bf18bc196f5b08bc5e4d0ae4bf18768342d5b48f90d27c5eda7c200bb025a2de774ef9a8dfd0d75fd844969950a13f5b4320a250d4a124fd0beae2313ac372d0583b79543c07fe4a31afc144acf99238c31b3e0b9c6241225467d7ef6a633313309fa84669b737cda0632d2dd36ec901e1ae3e4a68210fa0b0fa02588eff6e72f73f015951e42f85868c622a8be41f3247a2140f3fc0a30d31f2379fd387240e5f306a17b035720f57dba876e1cd0d33bc87d966d620e65736370fc817ac4ff15db12147cc85c230ebef736047408e03ca8f0be6a642030b5627458aa1e65dd56737ab810b2a8b492586c42e193ce28d9aae97a13db0cc36d358f6efdfc379ee394140a6700fd99e86b8f7d3a594cceb4bbefff68ce15866cdfc9894097f08e89b66f8c30bffcc001044b0594922e11b8a055c9f6ee026ae31ac020370a1f3e06daa5a772272941a101c11714fe8d8e37d40c3122c61d8f610d194cf82da8419cfa1680eb14ee96b6686e9f209a00df36e96a9d6f842571b23c753c5577e9ed466bb585e8910722050a93a8463bb655ccac5d9900a31a460738ff88a624304a1e5b37fb9ffb23887000c006db10e2b4b8985770d88102ff55d36e7513f37acb4c5920fbd220fc20c8b9b1a83128b0c64bcf26f761bc01407ff23a9b11942b6034278ae1b4817169180989eef86eba3f781c6ed7c82b279089823170533c0791d57ca17f1beb9bd6fda0242993bf5be154fa2377340d0c0c8fcd0da22a25000793deaaf634421431ac4d940c6ccbe9265f52eafaa6660cffe9ce6f0ae13c38cb7ce5304e697ff3814a992c030ff909d9b33615c8f9be2f199990be4b96c115324f1f53c609d9c70c9a3d643973be4f407aa54cfcb7741124c48570a6a3906b2eaa017d1c4e98b1336932976e7ac4b2cc3ff548a3d5272ca9b66092c0be62c903cc5f6022fe892c37fbaaa5bba75f41e40b410b223b052ed39b580fcc936b9582cf7c479ce148f56a374df66ddb07dd6547ed67da09e72e7820676bd33cc3dcc778b5e5929677908c6f43d3f2e793b768cb2bb6c11cac258438c7630aa10ec88ac11e66c0855e0be63beebbc4a846a4210d3b16a1a45b07144aa8b8587d6d981683640bcdb7390599cc6b806869d01e425cc59f78d26e009412daa789b0872610de5d576f928321c80f83d64dd5cac4562cfbd72b56bd17165c7c60feb78c973289e4afbb58225262f916b2e189f9195aaaaef30f037d1a0b47a5e1ad425f16c581f4317b4a1cce0458eb64e01a2ce88067b6365f20570e7a6ef374c26e7df9d9b86f1a2ed3f3a6f151d108f24245346f7fbacc41e39d1b89bbc83ce93442bd733e4fb70c090135e71518d07f6f7cb5484256d973b5e107cadc46b3caecb4a0c85930d59631ae69c164b9b51d4af12b414da442ca6e2e2e905b3d35c848bc74803d93cdd1a52642f101ecb76adfbb324aab602cda06f4142f88ea187aa2d9bc2770fb55738bf1c22a0172dd3aaaf0756da7919ea8fd480bd1852cd97487ce18b5406f1e208103de94a56189a902b0e677d3ebb09b9b4403e78dbd0aa42f5e177f3c0e44bff9304af9cfc15d61d2b7172aaff7db8e31c401014a1d5637a540050a7c24f8ce92509be63c99f33c3e169be4e48c5c46c0d00536d73fdfaacd543fc05f62e194e64df3916521560c2e7c70651b0c8cd1711e13cf10ed6d7db7642d832dac4b6699b200a8bae01e5c271a7cb1dd55be7593252ec16d138fea76b7ab76eb2597f4847ffa61f1e5560f85d5b1dfb976384bd384137d0d599a996035b696364b2bbd910add9dd520e1d50d4acbaa28865a9eceb70caf6cfd4e46b787fcef171be5316b41061ad0f844d78c571948c0c3b9ca5b581e66da9006818668cb3024873fb6c209e388964e71e4e7481f60e98293614a491c4343db492b43eaf5acc1b24720b645f73a741b8082ec8e7f0940270bd337222e0e5f38ae454a0bbbb97e2684eb4c0f8665e2f5fb567a7fc8f46d42d76a87b9167d5caaa34cac8c6c35e92f339daa280b074923950ca4fbe26d12fc08df611b122b294ee8165d619809c84a8483ac031a91a321ba578cfff08ff1da3638da881a658ee5025ca8cd4c2412bd64fec5acce4a063c903a928a1bbe47a19d6f20ce2a45e48b775e956e7328e2807bdf0699717624dcc4bea3f49696076d6bd1f3b32648c84b627afc98ee7a255a9b6a7688bc2cb1d022b456c914d73c31a61cdf1f0990d46e50809e625fee7f66f13f3fd3d711053420474e2137299fb499c55d280fadb264bfa1c01fb103c85e1025144416fb45c410b1de94ce3b6b39e31dbdd40085c18596d5a4f3aa5d72f5d762f32657466c3d6cfa119e4b15d66c166f9235130b6b5db7bdeb688674c2c9ffe5953f913dc8b70c89ee3a3e9d0b900d3eb11b0c998e9028b8af68a3a123f0a5aa56f987cb287bed1b4ec7ea4d5080f7460191140fbbc521c4b4ca284d2f52ba94d12c57cffdf6b3a1823c8bec3f227f0964d0269567969b2a6952d67b2cf3fe4ed64ad32021de8df302d6fa0e5934dcb0fe0207c4eaf81924185e0ae50fa740e900c098f17483a8c8e9acbd142b0d3b8d7ed31222b7d9d9642133a4d710850b29be1b312f2043e0553468a03a468da16119a0147bc6655daaeeaccef77df33300f0304b229d0a67f2e2686ed0faf697a8c0be02a92c5eecec61f86730c46d3e4060cedea01e6ad69edab787224daf217fc00d25bc51eaadc89bea9ed8b8b064d00a603bba923afd50feeaa28480fe0bf5208e1f6c3c1ab06c7581870abe1d4263967bca1c83e6d829d9967b15b03c71662b051fef32423b6f60529ef22b34e79fd39a03188920fb378c832b94ae04ee9c1e611c19e994f3fa9a123454d8453ff1ab96e3dfaf32adcce9ce3a58d3ab46800283017c41df169d9030faa8635070293a6b4f92c2a03a23f322f9219eea5c13a65e16f8cfbb4c12898a9b5666c005ee1bfaa0d64396e1ffb0c89394ac75ce404e341592c70ec8ef696ac200a491dc290b060b0d8ab0b0d73a785117cb992fd5082920edf8a802617e40021efd904982120cb763677cfbf5fb26e040250ff71a9e8f0a74f8c8c0cbb2d83b70592dcad430759e5e3365da322b45198c34067f1ef09108461a7aff586e522eff5dd06ba39f32adbbb2ac11cb5ed218b080ef6b47ab0a21d7658d2c57a0b782547f8c5acce7c340cbc4e32291a4f4d261d51eb5d19fc313f13da04d155cee22bdda2a05328dfad96d058d2752e41372178a8ac3069d27277f5ad615312c8a0da2d6f5967a6191fdfa66a465ba370e3d8a8b27f31b75cc1d1dfc9e914df09e8044c0d753950f5a662bdec0dc6b50c3fac197df947b2fe927b02824b638eba85cc6b3a3a4633ea595e9e7c01764122a2a188c7f05b86a0901781fe9cf000e194128b474d8334de7204e7cf9ba4c45a82851cc873cb2d851257d3c067a363b22d3ffc4592989e1cdd50333e7d20858eaded8fa02b85209c329cb4ba3f99922719ac16e761fee300db4c4022070de18149af7d30df3be006208f5703c677000acfdbdab00aa099e0c515cdd6c36e4c2fcb14d4a169ac5226206d780418161648be96d949099a7cd18b491d72cb00baf6a36552c49578cb858004493fa5ffabec5316a34f8af4e3e329c28ace755c8329add179dd15a716fc30cae70fd816fb9ae67f0e595a39585efe683dac6569cecd5b4612600ca7e58142774ac04ad50ebf3fd3ffbe32cfdacc60c4942daa5b1dbd52cb4570ce3acffe92365dd10cb6526daa7ba1a0b8635167f7220c97ce29b11fff0e2780c3d43d82700b6511664384bafbc8ace53cb17529a7cbe4fa1e5b8242a149af5120092d5f50af044e091387602eb00690a21ed41b2b6cd2ea58ffd23c273f07932cea2d0e81664d0b1770c1a8726cbf09e109bb62cf0bbd05bd068eeb95e2f120470b4082e0f037bed194d5aaedd71fdb36b71ebabfc29ba3fc10086cf780c1cdc74ebe0250fa517cbde50830063cefcc39e7848ae2f4ca899903296fefa908e8c18e1bb8a1437ac612ce96cf6c0617a6cd6c6e8a631315a1d3e29d803b558c3a4274960d005b212c9c31e65649812c0dc066ae224f5001a4893fdad31cf49271b8df0353c2feb0c6796198d5ddff04951416833ce4200813f0f39d2ce75da2985d4329c1f2b2023d34a62804670f522d449b1edbf12c255673077c821683b1d8d7e5ddf800a4b7b539cba5dcaf1fdb336daec57ba5cc8e484c04b5e70f74ee3a0a2e954e2175117e97ac2c5bdc2dd375946a0355561c2cb8edb457d5185a93189e13a4aea066c51edf64ec0704d127db99b2ce23ca840435bc4ce9d8b533e23bd09c80f5004b23d8666c77a078368ad040c4e1ce3d4fddf9ed1b1863c9ed1d2eea60ed85f24edbcea0b513edb75f6321e428db3b3082154a7923b2776b96f9276f251b155188c9415d7e4f09e37f89fbaddef8410979efa36c6a1efe5565349a96498aea518ac498adad8a301e7d171c35883c89798ca890032253d77b00393525dfc1c992b73dcd9fdf3f304f8b5c1f7a71f4036d4095ffca43ba1c2ed25761063f0ee3925fcb36659ca28e692b6f5e89599405f990c57ec7237311d663bdafde7d2f81d0dcc1c522e2497eb105f9d9bb4f969cb1c89c2c4010dead0a19023f4bf15879c2f13afbd1caf80f6e3b5628c1e36f57189ec2f5a61d03eaa58e379df3027da6f24403ef8d186c62c5176573cdb2e799c65bb6c0da4fcaba21d4b8c02afeb6b8717fe4b74f45e45025aaacd2ee55901d81ab63d04853f222695e0969e3a9f89b9297aa44364a7a2bcd0be5759f3a27bc00d7a99f32b69cce48cbba3cc858a9fc708deb0b9515755e48edb793a598de521bfecb827ca3fe1e58b9875426b76a268137ba4e5fe24a5d7f977fe9d1b50ce573fe98be883ce057ff046e26d999403f52ff08e8a7dfb7f993a389212ea2ed1bf7725f4a265bdbfc208fcda3df33465c90928f1e1bb77db1955ef73583031893947c902eecff3fe24903319d5c2433e0b0546305540bd96a71fda765fcad36af678a4f07cc95fcd02e81ac2760931d40c132c998a8c0b120abd2d3e666518d1ef5ffa130cca13b78e57cad7a151bb594a1e394e73d71aea3c2d6e5cbbff872c98714fb5fb571e1e3743eea704b295666d21ce4e84ca11a1d673a27b169c5f83088e0ed344fd75dd4b741b05226ab93513305f4a7f83ded532c62d697e04ba2a963e30c321f916c0080e9fbc5ab619aa8c220f38de69ea6d5a49d99f904ace4f9c6132a8708e225fa2ab197c4508ff25c42a92d2a7cc3855cd59f6c96407266922cfb6f523d46e47e29d0447f32582e1951a6b0361e26226f8379a50f2d5e3204c6828da40b8e7cdc816f6b166ef4aa723077f7be16b732f1edab20a5668e50844b717dacc74dd94451cb9460cca88ad8a22ff47083c488ffc04e3b9c39ec7b25f41ee2a2a6cae755bc5d1fb9e1633549013968d569bb6c6f0d95547d93eba03a1da40766f3ab3b18f19b30964af1d69db15b13c2024eb855dd70af579f46fad36a77a77c2cf65812e92125f2f9e4011eb2ccf4716ddca655668da9838e29677662101824afbe875bd3490fa1a7a286ae31704b40587eec531af8bf89a26692686387927daec1d6e004fe9105160219a7d23035a362fa532f57b6269292058de35d8cd36c4c6b1143821adbe16379d11e1232646f4d497651f0954ea9a1df894dfc491003980f60b52cc0a1ab26756077227ee3102ea57dce3271965fbc8bd6e66a68a2b9543b577278024cd4536b2741226c3b1d409828f4e0d9874ecd7069da3c9c77f290915adcdf6b456983796b3fc28042c760483b93e07a9ac85a3991713d1c2f85b94556c6d1a273da1ee90265d01a8981245c7f7f8d10b2915126d9f9ac61a2d6a8c055ea87be9bafcb5643c9b119120d465b5ddb005f7ccba998fd30058ffa33d82c9d8e4e301e780bd7f93832604cfea9de23f1d6b2a50f65047ac9f879eff0451f311164941d680b76395aa208016ec3c5dfd1e4e2451ec13fb58f2242b1c26e54bbd88b44e163e4e7c7aad0b3d5897b5f0d8d49522bd7b5c1aeabc1faa5f92051b7c9609bf3dabb085a533130bb3f740556f8e71453758b79613a124d615ab01b2e1de8a4e519947fcd62b059aca90452fd3b086e6f37ec6b9c8c565885d7bc2125f2a6edc128992c986ed1e16fd93e88a03407f497ac742f49836ac0886ff6354b8a0d7096dd464cbbd9e1182fbc13662c46028cf859e93de493b32a8060828596b2fc4245655a4f9589c0c3e6a3130d7703084bca793e15a9e9d6366f1898695e5d47b7da2fe660461450c47a7186e240f9f600739c2fa2038f28aa34075563b42e39189fff917f77b1d169abf6085508100e18e9651f68113f0371c993ba004d5ac582763655c4df79c28668406aa60c8966a70355446b5ef97eca8f9c21cd311efbd65d6701932662d214ba804cb02a5d0c4e936c50435a6d8a2678558c26aedd38694ee726b9fd89a039a0378b17166714bb1379bb173d2e916411278d8285e9e6dc901a616904b1927280ff562b631da5d3cc81ff70e3269a358e8c4dca3696ccdb546ae1147014621b472511388d61cb1e60590ad87f05617f122417a4282d94bbc643f0547a6682008695507d53505be0bd9b78ed3c7adb01360df7cc3b4e8ac5357485facb49c2e0600e9a923c71d12fcaffcddc8de640403018a9a2ff1c2ebcdfd6700facbbb2dbc42db37fa4e0b64fa45559ed421cf9e91bbb8533983393113a75023ebc8090bb50f8d0b75903c3c8e4b51137f3ccf2b5ee42e549fa6c2c57a336718e1867420e656c75e985702a0b1d78e451a4d0e7abfe681ecfd92e1fe5d00c00a22acdd0ff415858ee7fa9d195fbd1071d672106427bcd2c8f5114e6b4e3b8f6fba28c62b38318cbd8088a2b956ddcdebbcfe0e6670d37b63276fa30a16791a3fee1b9d", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4411dfdc7cb6265f100524012f038ee1f205bf8789b70b46c57463dedb4998f7ac800000000000000000000000000000000c6895e67e144556602cbe13943c8169600000000000000000000000000000000808b3aef60f3e74a629e9331d68a14b21806572c19ee2f3532e970d617919b3aa8cdc582782dfad0699badc631f75128000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022c17de150116bdca2a3edbbac4f77678c67c35f056f9e969387b99b5c26da55509a23e762ede2ab19d289955f6fc34a289c4196b6f4fed818bca760158098e851b1e385ae72fb3add3fe838e63d0e080c2409bd2fe5f57f926a01a4e31278a1e0fd8fef8ee3dacae5dd4bd8666f29088e7e2c1680f50451cf263d3e53ba2b8b4000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } diff --git a/packages/enclave-contracts/test/fixtures/dkgAttestation.ts b/packages/enclave-contracts/test/fixtures/dkgAttestation.ts new file mode 100644 index 0000000000..fafd5a62c8 --- /dev/null +++ b/packages/enclave-contracts/test/fixtures/dkgAttestation.ts @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import type { Signer } from "ethers"; + +import { ethers } from "./connection"; + +export type DkgFoldAttestation = { + partyId: number; + skAggCommit: string; + esmAggCommit: string; + signature: string; +}; + +export type DkgPartySlotBinding = { + partyId: number; + node: string; +}; + +/** Public inputs layout expected by `DkgFoldAttestationVerifier` for honest count `h`. */ +export function encodeMockDkgProofForAttestation( + pkCommitment: string, + partyIds: number[], + skCommits: string[], + esmCommits: string[], +): string { + const h = partyIds.length; + const publicInputs: string[] = Array.from( + { length: 6 + 3 * h }, + () => ethers.ZeroHash, + ); + publicInputs[publicInputs.length - 1] = pkCommitment; + for (let i = 0; i < h; i++) { + publicInputs[2 + i] = ethers.zeroPadValue(ethers.toBeHex(partyIds[i]), 32); + publicInputs[5 + h + i] = skCommits[i]; + publicInputs[5 + 2 * h + i] = esmCommits[i]; + } + return ethers.AbiCoder.defaultAbiCoder().encode( + ["bytes", "bytes32[]"], + ["0x", publicInputs], + ); +} + +/** Sign one EIP-712 fold-attestation tuple. */ +export async function signFoldAttestation( + signer: Signer, + chainId: bigint, + verifyingContract: string, + e3Id: number, + partyId: number, + skAggCommit: string, + esmAggCommit: string, +): Promise { + const domain = { + name: "EnclaveDkgFoldAttestation", + version: "1", + chainId, + verifyingContract, + }; + const types = { + DkgFoldAttestation: [ + { name: "e3Id", type: "uint256" }, + { name: "partyId", type: "uint256" }, + { name: "skAggCommit", type: "bytes32" }, + { name: "esmAggCommit", type: "bytes32" }, + ], + }; + return signer.signTypedData(domain, types, { + e3Id, + partyId, + skAggCommit, + esmAggCommit, + }); +} + +/** + * Build mock proof + bundle payloads for `publishCommittee`/verifier tests. + * Operators are sorted by address to match on-chain canonical `topNodes`. + */ +export async function buildMockDkgAttestationFixtureData( + operators: Signer[], + e3Id: number, + pkCommitment: string, + signingVerifierAddress: string, +): Promise<{ + ordered: { op: Signer; addr: string }[]; + proof: string; + bundle: string; + partyIds: number[]; + skCommits: string[]; + esmCommits: string[]; + attestations: DkgFoldAttestation[]; + bindings: DkgPartySlotBinding[]; +}> { + const ordered = await Promise.all( + operators.map(async (op) => ({ op, addr: await op.getAddress() })), + ); + ordered.sort((a, b) => + a.addr.toLowerCase() < b.addr.toLowerCase() + ? -1 + : a.addr.toLowerCase() > b.addr.toLowerCase() + ? 1 + : 0, + ); + + const partyIds = ordered.map((_, idx) => idx); + const skCommits = partyIds.map((i) => ethers.id(`sk-${e3Id}-${i}`)); + const esmCommits = partyIds.map((i) => ethers.id(`esm-${e3Id}-${i}`)); + const proof = encodeMockDkgProofForAttestation( + pkCommitment, + partyIds, + skCommits, + esmCommits, + ); + + const { chainId } = await ethers.provider.getNetwork(); + const attestations: DkgFoldAttestation[] = []; + const bindings: DkgPartySlotBinding[] = []; + for (let i = 0; i < ordered.length; i++) { + attestations.push({ + partyId: i, + skAggCommit: skCommits[i], + esmAggCommit: esmCommits[i], + signature: await signFoldAttestation( + ordered[i].op, + chainId, + signingVerifierAddress, + e3Id, + i, + skCommits[i], + esmCommits[i], + ), + }); + bindings.push({ partyId: i, node: ordered[i].addr }); + } + + const bundle = ethers.AbiCoder.defaultAbiCoder().encode( + [ + "tuple(uint256 partyId, bytes32 skAggCommit, bytes32 esmAggCommit, bytes signature)[]", + "tuple(uint256 partyId, address node)[]", + ], + [attestations, bindings], + ); + + return { + ordered, + proof, + bundle, + partyIds, + skCommits, + esmCommits, + attestations, + bindings, + }; +} + +/** Convenience helper for Enclave tests with a plaintext public key input. */ +export async function buildMockAggregationPublishArgs( + operators: Signer[], + e3Id: number, + publicKey: string, + signingVerifierAddress: string, +): Promise<{ proof: string; bundle: string }> { + const fixture = await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.keccak256(publicKey), + signingVerifierAddress, + ); + return { proof: fixture.proof, bundle: fixture.bundle }; +} diff --git a/packages/enclave-contracts/test/fixtures/helpers.ts b/packages/enclave-contracts/test/fixtures/helpers.ts index 609dfbf97a..f0667be84a 100644 --- a/packages/enclave-contracts/test/fixtures/helpers.ts +++ b/packages/enclave-contracts/test/fixtures/helpers.ts @@ -39,6 +39,7 @@ export const setupAndPublishCommittee = async ( publicKey: string, operators: Signer[], committeeProof: string = "0x", + dkgAttestationBundle: string = "0x", ): Promise => { for (const operator of operators) { await registry.connect(operator).submitTicket(e3Id, 1); @@ -51,6 +52,7 @@ export const setupAndPublishCommittee = async ( publicKey, pkCommitment, committeeProof, + dkgAttestationBundle, ); }; diff --git a/packages/enclave-contracts/test/fixtures/index.ts b/packages/enclave-contracts/test/fixtures/index.ts index 386b65134e..a8e988565c 100644 --- a/packages/enclave-contracts/test/fixtures/index.ts +++ b/packages/enclave-contracts/test/fixtures/index.ts @@ -6,6 +6,7 @@ export { VOTE_TYPEHASH, signAndEncodeAttestation } from "./attestation"; export { connection, ethers, ignition, networkHelpers } from "./connection"; +export * from "./dkgAttestation"; export * from "./constants"; export * from "./helpers"; export { setupOperatorForSortition } from "./operators"; diff --git a/scripts/README.md b/scripts/README.md index 68bd8debfa..f94c39118c 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -243,12 +243,41 @@ Circuits are built locally and stored in a git branch: ## Verifier Generator -`generate-verifiers.ts` - Generates Solidity verifier contracts from compiled Noir circuits. +`generate-verifiers.ts` - Generates (or verifies) Solidity Honk verifier contracts from compiled +Noir circuits. + +The generated `.sol` files under `packages/enclave-contracts/contracts/verifiers/bfv/honk/` are +**committed to git** and correspond to **exactly one BFV preset**: `insecure-512` (the development / +CI / benchmark default). The Honk verifiers bake in the recursive VKs of `dkg_aggregator` / +`decryption_aggregator`, which are preset-dependent — different BFV parameter sets compile to +different VKs and therefore different `.sol` bytes. The committed files only match `insecure-512`. + +The generator enforces this: both `--check` and `--write` refuse to run unless +`dist/circuits/insecure-512/.build-stamp.json` exists and reports `"preset": "insecure-512"`. The +stamp is written by [`pnpm build:circuits --preset `](#circuit-builder) and is the only +on-disk record of which preset built `circuits/bin/`. If a different preset was last built (or +none), the generator refuses with a clear fix recipe instead of silently producing the wrong `.sol`. +If you need verifiers for a different preset (e.g. a production deploy on `secure-8192`), generate +them locally for that deploy — do **not** commit the result over the canonical files. + +The script has two modes: + +- **`--check` (used by test/benchmark/CI flows)** — regenerate in memory and diff against the + committed files. Exits non-zero on drift without touching the working tree. This is how + `tests/integration/lib/prebuild.sh`, `circuits/benchmarks/scripts/extract_crisp_verify_gas.sh`, + and `circuits/benchmarks/scripts/replay_folded_verify_gas.sh` invoke the script — so accidental + drift between committed verifiers and current circuit VKs surfaces as a failure rather than a + silent rewrite mid-test. +- **`--write` (default for manual runs)** — regenerate and overwrite the committed files. Use this + when you intentionally bump the canonical-preset circuits or the Noir/bb toolchain. ### Usage ```bash -# Generate verifiers for all circuits +# Verify committed verifiers match current circuit VKs (CI/tests use this) +pnpm generate:verifiers --check + +# Regenerate (default; equivalent to --write) pnpm generate:verifiers # Generate only for specific group @@ -259,7 +288,7 @@ pnpm generate:verifiers --group threshold pnpm generate:verifiers --circuit pk pnpm generate:verifiers --circuit pk --circuit fold -# Clean existing verifiers before generating +# Clean existing verifier directory first (write mode only) pnpm generate:verifiers --clean # Preview what would be generated @@ -281,16 +310,55 @@ Automates the full pipeline from Noir circuits to on-chain Solidity verifiers: - Renames contract from `HonkVerifier` to descriptive name (e.g., `DkgAggregatorVerifier`, `DecryptionAggregatorVerifier`) - Replaces Apache-2.0 license header with LGPL-3.0-only -6. **Outputs** to `packages/enclave-contracts/contracts/verifiers/bfv/honk/` + - Runs `prettier-plugin-solidity` so on-disk format matches the rest of the repo (and so + `--check` doesn't trip on whitespace differences vs. raw `bb` output) +6. **Outputs / verifies** at `packages/enclave-contracts/contracts/verifiers/bfv/honk/`: + - In `--write` mode: overwrites the committed `.sol` files. + - In `--check` mode: diffs the freshly generated content against the committed `.sol` and exits + non-zero on any drift, printing the offending files and a fix recipe. + +### When `--check` (or `--write`) fails + +There are two distinct failure modes — the error output tells you which one: + +**1. Canonical preset not built** — the generator refuses up front because +`dist/circuits/insecure-512/.build-stamp.json` is missing or reports a different preset. The +committed verifiers are pinned to `insecure-512`; nothing under `circuits/bin/` is trusted unless +the build stamp confirms the canonical preset was last built. + +To fix: + +```bash +pnpm build:circuits --preset insecure-512 +# then retry the original command +``` + +**2. Drift between committed verifiers and current circuit VKs** — the canonical preset is built but +the bytes don't match. Typical causes: + +- You ran `pnpm build:circuits` against a different Noir/bb version than the one that produced the + committed verifiers (see `crates/zk-prover/versions.json` for the pinned versions). +- A circuit was changed without regenerating the committed Solidity files. + +To fix: + +1. Verify your `nargo` / `bb` versions match `crates/zk-prover/versions.json`. +2. Run `pnpm build:circuits --preset insecure-512`. +3. Run `pnpm generate:verifiers --write`. +4. Commit the resulting diff under `packages/enclave-contracts/contracts/verifiers/bfv/honk/`. ### Options The `generate:verifiers` script in package.json passes `--circuits` with the on-chain used list. +- `--check` - Verify committed verifiers match current VKs (no writes). Exits non-zero on drift. +- `--write` - Write/overwrite committed verifiers. Default when neither `--check` nor `--write` is + passed. - `--circuits ` - Circuit names, comma-separated. Omit to generate all. - `--group ` - Circuit groups (comma-separated: dkg,threshold,recursive_aggregation) -- `--clean` - Remove existing verifier directory before generating +- `--clean` - Remove existing verifier directory before generating (write mode only) - `--no-compile` - Don't compile circuits automatically (fail if not already compiled) +- `--no-clean-targets` - Don't delete nargo target dirs before generating verifiers - `--dry-run` - Show what would be generated without doing anything - `-h, --help` - Show help message diff --git a/scripts/build-circuits.ts b/scripts/build-circuits.ts index e9d093861e..232a8e79ee 100644 --- a/scripts/build-circuits.ts +++ b/scripts/build-circuits.ts @@ -57,6 +57,8 @@ interface BuildOptions { clean?: boolean noCleanTargets?: boolean skipIfBuilt?: boolean + /** Copy dist/circuits// artifacts into circuits/bin without nargo compile. */ + hydrateBinOnly?: boolean dryRun?: boolean preset?: CircuitPreset | 'all' } @@ -177,13 +179,67 @@ class NoirCircuitBuilder { writeFileSync(this.presetStampPath(preset), JSON.stringify(stamp, null, 2) + '\n') } - /** Marker files required by `test_trbfv_actor` / gas extraction (dist + circuits/bin targets). */ - private requiredPresetMarkers(preset: string): string[] { + /** Records which BFV preset last populated `circuits/bin/` (used by benchmark gas extraction). */ + private writeActiveBinPresetStamp(preset: string, sourceHash: string): void { + const stamp: PresetBuildStamp = { + preset, + sourceHash, + builtAt: new Date().toISOString(), + } + writeFileSync(join(this.circuitsDir, '.active-preset.json'), JSON.stringify(stamp, null, 2) + '\n') + } + + /** + * Point circuits/bin at a preset already archived under dist/circuits//. + * Used when dist is fresh but bin still holds another preset (common after --mode insecure + * then --mode secure benchmark runs). + */ + private hydrateBinFromDist(preset: string, sourceHash: string): void { + const distRoot = join(this.options.outputDir!, preset) + const circuits = this.discoverCircuits() + let copied = 0 + + for (const circuit of circuits) { + const packageName = this.getPackageName(circuit.path) + const targetDir = join(circuit.path, 'target') + mkdirSync(targetDir, { recursive: true }) + + const copyPair = (from: string, to: string) => { + if (!existsSync(from)) return + copyFileSync(from, to) + copied++ + } + + const defaultDir = join(distRoot, CIRCUIT_VARIANTS.DEFAULT, circuit.group, circuit.name) + copyPair(join(defaultDir, `${packageName}.json`), join(targetDir, `${packageName}.json`)) + copyPair(join(defaultDir, `${packageName}.vk`), join(targetDir, `${packageName}.vk_recursive`)) + copyPair(join(defaultDir, `${packageName}.vk_hash`), join(targetDir, `${packageName}.vk_recursive_hash`)) + + const evmDir = join(distRoot, CIRCUIT_VARIANTS.EVM, circuit.group, circuit.name) + copyPair(join(evmDir, `${packageName}.vk`), join(targetDir, `${packageName}.vk`)) + copyPair(join(evmDir, `${packageName}.vk_hash`), join(targetDir, `${packageName}.vk_hash`)) + + const recursiveDir = join(distRoot, CIRCUIT_VARIANTS.RECURSIVE, circuit.group, circuit.name) + copyPair(join(recursiveDir, `${packageName}.vk`), join(targetDir, `${packageName}.vk_noir`)) + copyPair(join(recursiveDir, `${packageName}.vk_hash`), join(targetDir, `${packageName}.vk_noir_hash`)) + } + + console.log(` Copied ${copied} artifact file(s) into circuits/bin targets.`) + this.writeActiveBinPresetStamp(preset, sourceHash) + } + + private requiredDistMarkers(preset: string): string[] { const dist = join(this.options.outputDir!, preset) - const bin = this.circuitsDir return [ join(dist, CIRCUIT_VARIANTS.DEFAULT, CIRCUIT_GROUPS.AGGREGATION, 'dkg_aggregator', 'dkg_aggregator.json'), join(dist, CIRCUIT_VARIANTS.DEFAULT, CIRCUIT_GROUPS.AGGREGATION, 'decryption_aggregator', 'decryption_aggregator.json'), + ] + } + + /** Marker files required by `test_trbfv_actor` / gas extraction under circuits/bin. */ + private requiredBinMarkers(): string[] { + const bin = this.circuitsDir + return [ join(bin, CIRCUIT_GROUPS.AGGREGATION, 'dkg_aggregator', 'target', 'dkg_aggregator.json'), join(bin, CIRCUIT_GROUPS.AGGREGATION, 'dkg_aggregator', 'target', 'dkg_aggregator.vk_recursive'), join(bin, CIRCUIT_GROUPS.AGGREGATION, 'decryption_aggregator', 'target', 'decryption_aggregator.json'), @@ -193,10 +249,30 @@ class NoirCircuitBuilder { ] } - private isPresetUpToDate(preset: string, sourceHash: string): boolean { + private readActiveBinPreset(): PresetBuildStamp | null { + const activePath = join(this.circuitsDir, '.active-preset.json') + if (!existsSync(activePath)) return null + try { + return JSON.parse(readFileSync(activePath, 'utf-8')) as PresetBuildStamp + } catch { + return null + } + } + + private isDistPresetUpToDate(preset: string, sourceHash: string): boolean { const stamp = this.readPresetStamp(preset) if (!stamp?.sourceHash || stamp.sourceHash !== sourceHash) return false - return this.requiredPresetMarkers(preset).every((path) => existsSync(path)) + return this.requiredDistMarkers(preset).every((path) => existsSync(path)) + } + + private isBinReadyForPreset(preset: string, sourceHash: string): boolean { + const active = this.readActiveBinPreset() + if (!active || active.preset !== preset || active.sourceHash !== sourceHash) return false + return this.requiredBinMarkers().every((path) => existsSync(path)) + } + + private isPresetUpToDate(preset: string, sourceHash: string): boolean { + return this.isDistPresetUpToDate(preset, sourceHash) && this.isBinReadyForPreset(preset, sourceHash) } private logSkipIfBuiltBlocked(preset: string, sourceHash: string): void { @@ -212,7 +288,7 @@ class NoirCircuitBuilder { `Run without --skip-if-built or \`pnpm build:circuits --preset ${preset}\` once to refresh.`, ) } - const missing = this.requiredPresetMarkers(preset).filter((path) => !existsSync(path)) + const missing = [...this.requiredDistMarkers(preset), ...this.requiredBinMarkers()].filter((path) => !existsSync(path)) if (missing.length > 0) { console.log(` ℹ️ --skip-if-built: missing ${missing.length} marker artifact(s), e.g. ${missing[0]}`) } @@ -244,14 +320,35 @@ class NoirCircuitBuilder { const sourceHash = this.computeSourceHash(preset) result.sourceHash = sourceHash + if (this.options.hydrateBinOnly) { + if (!this.isDistPresetUpToDate(preset, sourceHash)) { + throw new Error( + `Cannot hydrate circuits/bin: dist/circuits/${preset} is missing or stale. ` + `Run: pnpm build:circuits --preset ${preset}`, + ) + } + console.log(` 💧 Hydrating circuits/bin from dist/circuits/${preset} (no nargo compile)...`) + this.hydrateBinFromDist(preset, sourceHash) + console.log(`\n✅ Hydrated circuits/bin for preset: ${preset}`) + return result + } + if (this.options.skipIfBuilt) { if (this.isPresetUpToDate(preset, sourceHash)) { console.log( - ` ⏭️ Skipping preset ${preset} (artifacts up to date; source_hash=${sourceHash}). ` + + ` ⏭️ Skipping preset ${preset} (dist + circuits/bin up to date; source_hash=${sourceHash}). ` + `Use a full rebuild without --skip-if-built to refresh.`, ) return result } + if (this.isDistPresetUpToDate(preset, sourceHash)) { + console.log( + ` 💧 dist/circuits/${preset} is current; hydrating circuits/bin from dist ` + + `(fast — avoids a full ~50m secure recompile when switching presets).`, + ) + this.hydrateBinFromDist(preset, sourceHash) + console.log(`\n✅ Hydrated circuits/bin for preset: ${preset}`) + return result + } this.logSkipIfBuiltBlocked(preset, sourceHash) } @@ -259,6 +356,10 @@ class NoirCircuitBuilder { this.setNoirConfigPreset(modNrPath, preset) } + if (modNrPath) { + this.setNoirConfigPreset(modNrPath, preset) + } + if (!this.options.noCleanTargets) { this.cleanTargetDirs(circuits) } @@ -276,6 +377,7 @@ class NoirCircuitBuilder { this.copyArtifacts(result.compiled, presetOutputDir, preset) if (result.errors.length === 0) { this.writePresetStamp(preset, sourceHash) + this.writeActiveBinPresetStamp(preset, sourceHash) } console.log(`\n✅ Built ${result.compiled.length} circuits for preset: ${preset}`) if (result.errors.length > 0) { @@ -753,6 +855,7 @@ async function main() { else if (arg === '--no-clean') options.clean = false else if (arg === '--no-clean-targets') options.noCleanTargets = true else if (arg === '--skip-if-built') options.skipIfBuilt = true + else if (arg === '--hydrate-bin-only') options.hydrateBinOnly = true else if (arg === '--group') options.groups = args[++i]?.split(',') as CircuitGroup[] else if (arg === '--circuit') (options.circuits ??= []).push(args[++i]) else if (arg === '-o' || arg === '--output') options.outputDir = resolve(args[++i]) @@ -796,7 +899,8 @@ Options: --dry-run Show what would be built --no-clean Don't clean output directory --no-clean-targets Don't delete circuits/bin target dirs before compiling - --skip-if-built Skip preset when dist stamp + marker artifacts match circuit sources + --skip-if-built Skip preset when dist + circuits/bin match; hydrate bin from dist if only dist is current + --hydrate-bin-only Copy dist/circuits// into circuits/bin (no nargo compile) -h, --help Show help `) } diff --git a/scripts/generate-verifiers.ts b/scripts/generate-verifiers.ts index 88a556869f..fba43940f0 100644 --- a/scripts/generate-verifiers.ts +++ b/scripts/generate-verifiers.ts @@ -6,7 +6,19 @@ // or FITNESS FOR A PARTICULAR PURPOSE. /** - * Generate Solidity verifier contracts from compiled Noir circuits. + * Generate (or verify) Solidity verifier contracts from compiled Noir circuits. + * + * The Honk Solidity verifiers are committed to git. To keep the committed + * files in sync with the recursive VKs, this script has two modes: + * + * - `--check` (default for test/benchmark flows): regenerate in memory and + * diff against the committed files. Exits non-zero on drift without + * touching the working tree. Used by `prebuild.sh`, `extract_crisp_verify_gas.sh`, + * `replay_folded_verify_gas.sh` so accidental drift fails loudly instead + * of silently rewriting committed contracts mid-test. + * + * - `--write` (default for manual runs): regenerate and overwrite the + * committed `.sol` files. Use this when you intentionally bump circuits. * * Prerequisites: * - `nargo` and `bb` (Barretenberg CLI) must be installed and in PATH @@ -14,17 +26,18 @@ * will compile them automatically. * * Usage: - * pnpm generate:verifiers # All circuits (or --circuits from package.json) - * pnpm generate:verifiers --circuits pk,fold # Specific circuits - * pnpm generate:verifiers --clean # Remove existing verifiers first - * pnpm generate:verifiers --dry-run # Show what would be generated - * pnpm generate:verifiers --no-compile # Use artifacts from build:circuits (skips target cleanup) + * pnpm generate:verifiers # Write all circuits (default) + * pnpm generate:verifiers --check # Verify committed verifiers match VKs (no writes) + * pnpm generate:verifiers --circuits pk,fold # Specific circuits + * pnpm generate:verifiers --clean # Remove existing verifiers first (write mode only) + * pnpm generate:verifiers --dry-run # Show what would be generated + * pnpm generate:verifiers --no-compile # Use artifacts from build:circuits (skips target cleanup) */ -import { execSync } from 'child_process' +import { execFileSync, execSync } from 'child_process' import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'fs' import { basename, join, resolve } from 'path' -import { ALL_GROUPS, CIRCUIT_GROUPS, type CircuitGroup } from './circuit-constants' +import { ALL_GROUPS, ALL_PRESETS, CIRCUIT_GROUPS, type CircuitGroup } from './circuit-constants' // --------------------------------------------------------------------------- // Types & constants @@ -37,6 +50,28 @@ const LICENSE_HEADER = `// SPDX-License-Identifier: LGPL-3.0-only // or FITNESS FOR A PARTICULAR PURPOSE. ` +/** + * Canonical BFV preset for the committed Honk Solidity verifiers. + * + * The on-chain `DkgAggregatorVerifier.sol` / `DecryptionAggregatorVerifier.sol` bake in the + * recursive VKs of `dkg_aggregator` / `decryption_aggregator`, which are + * **preset-dependent**: different BFV parameter sets compile to different VKs and therefore + * different `.sol` bytes. Exactly one preset can be "the committed one"; we pin `insecure-512` + * because that is the development/CI/benchmark default and the preset every committed verifier + * corresponds to. + * + * Both `--check` and `--write` refuse to run unless + * `dist/circuits//.build-stamp.json` exists and its `preset` field matches. + * This prevents silently producing/checking against the wrong preset's VKs — e.g. after + * `pnpm build:circuits --preset secure-8192`, where `circuits/bin/` holds secure artifacts that + * would generate different `.sol` bytes. + * + * If you need verifiers for a different preset (e.g. a production deploy on `secure-8192`), + * rebuild that preset locally and run the generator there; do **not** commit the result over + * the canonical files. + */ +const CANONICAL_PRESET = 'insecure-512' + interface CircuitInfo { name: string group: CircuitGroup @@ -51,6 +86,17 @@ interface GenerateOptions { dryRun?: boolean compile?: boolean // compile circuits before generating verifiers noCleanTargets?: boolean // skip deleting nargo target dirs before generation + /** + * Check mode: generate verifiers into memory and diff against the committed + * files. Exit non-zero on any drift. No writes to the working tree. + * Used by test/benchmark/CI flows that must not silently mutate committed + * verifier contracts. + */ + check?: boolean + /** BFV preset whose artifacts in `circuits/bin/` are used for generation/check. */ + preset?: string + /** Override output directory (write mode only). Defaults to committed honk/ path. */ + outputDir?: string } // --------------------------------------------------------------------------- @@ -66,7 +112,10 @@ class VerifierGenerator { constructor(rootDir?: string, options: GenerateOptions = {}) { this.rootDir = rootDir ?? resolve(__dirname, '..') this.circuitsDir = join(this.rootDir, 'circuits', 'bin') - this.verifierDir = join(this.rootDir, 'packages', 'enclave-contracts', 'contracts', 'verifiers', 'bfv', 'honk') + this.verifierDir = + options.outputDir !== undefined + ? resolve(options.outputDir) + : join(this.rootDir, 'packages', 'enclave-contracts', 'contracts', 'verifiers', 'bfv', 'honk') this.options = { groups: ALL_GROUPS, clean: false, @@ -76,11 +125,30 @@ class VerifierGenerator { } async generate(): Promise { - console.log('🔮 Generating Solidity verifiers from Noir circuits...\n') + const mode = this.options.check ? 'Checking' : 'Generating' + console.log(`🔮 ${mode} Solidity verifiers from Noir circuits...\n`) this.checkTool('nargo --version', 'nargo') this.checkTool('bb --version', 'bb') + const targetPreset = this.targetPreset() + + if (!this.options.dryRun) { + this.assertPresetBuilt(targetPreset) + this.assertCircuitsBinActivePreset(targetPreset) + } + + // Committed Honk `.sol` files are pinned to CANONICAL_PRESET only. Secure (and other) + // benchmark modes still need `circuits/bin/` aligned to their preset for integration + gas replay. + if (this.options.check && targetPreset !== CANONICAL_PRESET) { + console.log( + `\n✅ Preset '${targetPreset}' is built and active in circuits/bin.\n` + + ` Committed Honk verifiers in git are pinned to '${CANONICAL_PRESET}' only; skipping .sol diff.\n` + + ` Benchmark gas replay deploys fresh aggregator verifiers from circuits/bin at runtime.\n`, + ) + return + } + const circuits = this.discoverCircuits() if (circuits.length === 0) { console.log(' ⚠️ No circuits found') @@ -100,12 +168,20 @@ class VerifierGenerator { this.cleanTargetDirs(circuits) } - // Prepare output directory - if (this.options.clean && existsSync(this.verifierDir)) { - rmSync(this.verifierDir, { recursive: true }) - console.log(' 🗑️ Cleaned existing verifier directory') + if (this.options.check && this.options.clean) { + throw new Error('--check and --clean are mutually exclusive (check must not mutate the working tree)') + } + + // In write mode, prepare output directory. + if (!this.options.check) { + if (this.options.clean && existsSync(this.verifierDir)) { + rmSync(this.verifierDir, { recursive: true }) + console.log(' 🗑️ Cleaned existing verifier directory') + } + mkdirSync(this.verifierDir, { recursive: true }) + } else if (!existsSync(this.verifierDir)) { + throw new Error(`--check requires the committed verifier directory to exist: ${this.verifierDir}`) } - mkdirSync(this.verifierDir, { recursive: true }) // Pre-flight: two circuits with the same leaf name would silently overwrite each other's .sol. const seen = new Map() @@ -122,24 +198,63 @@ class VerifierGenerator { seen.set(contractFile, `${circuit.group}/${circuit.name}`) } - const generated: string[] = [] + const processed: string[] = [] + const drift: { circuit: string; file: string; reason: string }[] = [] const errors: string[] = [] for (const circuit of circuits) { try { - const solFile = this.generateVerifier(circuit) - generated.push(solFile) + const result = this.generateVerifier(circuit) + processed.push(result.outputPath) + if (this.options.check) { + if (!existsSync(result.outputPath)) { + drift.push({ + circuit: `${circuit.group}/${circuit.name}`, + file: result.outputPath, + reason: 'committed file is missing', + }) + } else { + const committed = readFileSync(result.outputPath, 'utf-8') + if (committed !== result.content) { + drift.push({ + circuit: `${circuit.group}/${circuit.name}`, + file: result.outputPath, + reason: 'content differs from committed file', + }) + } + } + } } catch (error: any) { errors.push(`${circuit.group}/${circuit.name}: ${error.message}`) console.error(` ✗ ${circuit.group}/${circuit.name}: ${error.message}`) } } - console.log(`\n✅ Generated ${generated.length} Solidity verifier(s) in:`) - console.log(` ${this.verifierDir}\n`) - - for (const f of generated) { - console.log(` • ${basename(f)}`) + if (this.options.check) { + if (drift.length > 0) { + console.error(`\n❌ ${drift.length} Solidity verifier(s) drift from current circuit VKs:`) + for (const d of drift) { + console.error(` • ${d.circuit} (${basename(d.file)}): ${d.reason}`) + } + console.error( + `\n The committed Honk verifiers under contracts/verifiers/bfv/honk are out of sync\n` + + ` with the circuits' recursive VKs. This usually means:\n` + + ` - You ran 'pnpm build:circuits' against a different Noir/bb version, or\n` + + ` - A circuit / VK changed without regenerating the committed Solidity files.\n` + + `\n To fix:\n` + + ` 1. Verify your Noir/bb versions match (see crates/zk-prover/versions.json).\n` + + ` 2. Run 'pnpm build:circuits --preset insecure-512' (or the relevant preset).\n` + + ` 3. Run 'pnpm generate:verifiers --write' (or omit --check) to refresh the\n` + + ` committed .sol files, then commit the diff.\n`, + ) + process.exit(1) + } + console.log(`\n✅ Checked ${processed.length} Solidity verifier(s) — all in sync with current VKs.\n`) + for (const f of processed) console.log(` • ${basename(f)}`) + } else { + console.log(`\n✅ Generated ${processed.length} Solidity verifier(s) in:`) + console.log(` ${this.verifierDir}\n`) + for (const f of processed) console.log(` • ${basename(f)}`) } if (errors.length > 0) { @@ -192,7 +307,7 @@ class VerifierGenerator { // Generation pipeline (compile → write_vk → write_solidity_verifier) // ------------------------------------------------------------------------- - private generateVerifier(circuit: CircuitInfo): string { + private generateVerifier(circuit: CircuitInfo): { content: string; outputPath: string } { const { name, group, packageName } = circuit // 1. Compile if needed @@ -204,13 +319,13 @@ class VerifierGenerator { // 3. Generate Solidity verifier const rawSolPath = join(targetDir, `${packageName}_verifier.sol`) - execSync(`bb write_solidity_verifier -k "${vkPath}" -o "${rawSolPath}"`, { stdio: 'pipe' }) + execFileSync('bb', ['write_solidity_verifier', '-k', vkPath, '-o', rawSolPath], { stdio: 'pipe' }) if (!existsSync(rawSolPath)) { throw new Error('bb write_solidity_verifier did not produce output') } - // 4. Post-process: rename contract, add license header, copy to output + // 4. Post-process: rename contract, add license header const contractName = this.toContractName(name) const outputFileName = `${contractName}.sol` const outputPath = join(this.verifierDir, outputFileName) @@ -223,13 +338,40 @@ class VerifierGenerator { // Replace license header – bb produces Apache-2.0 by default solidity = solidity.replace(/\/\/\s*SPDX-License-Identifier:[^\n]*\n(\/\/[^\n]*\n)*/, LICENSE_HEADER) - writeFileSync(outputPath, solidity) - - // Clean up intermediate file + // Clean up intermediate file (always — we don't keep the bb temp output around) rmSync(rawSolPath, { force: true }) - console.log(` ✓ ${group}/${name} → ${outputFileName}`) - return outputPath + // Normalize with prettier so the on-disk format matches what the rest of + // the repo's `pnpm prettier:write` produces. Without this, --check would + // always fail because bb emits a different whitespace style than + // prettier-plugin-solidity. + solidity = this.formatSolidity(solidity, outputPath) + + // In check mode, do not touch the committed file. + if (!this.options.check) { + writeFileSync(outputPath, solidity) + console.log(` ✓ ${group}/${name} → ${outputFileName}`) + } else { + console.log(` • ${group}/${name} → ${outputFileName} (checking)`) + } + + return { content: solidity, outputPath } + } + + /** + * Format Solidity through prettier-plugin-solidity so output matches the + * project's standard formatting. Run from `packages/enclave-contracts` so + * prettier picks up the local `.prettierrc` and plugin resolution. + */ + private formatSolidity(content: string, outputPath: string): string { + const contractsDir = join(this.rootDir, 'packages', 'enclave-contracts') + // Use prettier --stdin-filepath so plugin selection is by extension. + const result = execFileSync('pnpm', ['exec', 'prettier', '--stdin-filepath', outputPath], { + cwd: contractsDir, + input: content, + stdio: ['pipe', 'pipe', 'pipe'], + }) + return result.toString('utf-8') } /** @@ -278,7 +420,7 @@ class VerifierGenerator { } // Generate VK (EVM target for Solidity verifiers) - execSync(`bb write_vk -b "${jsonFile}" -o "${targetDir}" -t evm`, { stdio: 'pipe' }) + execFileSync('bb', ['write_vk', '-b', jsonFile, '-o', targetDir, '-t', 'evm'], { stdio: 'pipe' }) // bb writes to 'vk' by default, rename to .vk if (existsSync(defaultVk) && !existsSync(vkFile)) { @@ -358,6 +500,75 @@ class VerifierGenerator { throw new Error(`${name} is not installed or not in PATH`) } } + + private targetPreset(): string { + return this.options.preset ?? CANONICAL_PRESET + } + + /** + * Refuse to run unless `dist/circuits//.build-stamp.json` exists for the target preset. + */ + private assertPresetBuilt(preset: string): void { + const stampPath = join(this.rootDir, 'dist', 'circuits', preset, '.build-stamp.json') + if (!existsSync(stampPath)) { + throw new Error( + `Preset '${preset}' is not built (missing ${stampPath}).\n` + + `\n` + + ` To fix, run from the repo root:\n` + + ` pnpm build:circuits --preset ${preset}\n` + + ` then retry.`, + ) + } + let stamp: { preset?: string } = {} + try { + stamp = JSON.parse(readFileSync(stampPath, 'utf-8')) + } catch (err: any) { + throw new Error(`Failed to parse ${stampPath}: ${err.message}`) + } + if (stamp.preset !== preset) { + throw new Error( + `Build stamp at ${stampPath} reports preset '${stamp.preset ?? '(missing)'}', expected '${preset}'.\n` + + ` Run:\n` + + ` pnpm build:circuits --preset ${preset}\n` + + ` then retry.`, + ) + } + console.log(` ✓ Preset '${preset}' build stamp present in dist/circuits.\n`) + } + + /** + * Ensure `circuits/bin/` was last populated by `build:circuits` for the same preset. + * Without this, a secure benchmark could leave secure VKs in bin while `--check` diffs + * against committed insecure-512 `.sol` files. + */ + private assertCircuitsBinActivePreset(preset: string): void { + const activePath = join(this.circuitsDir, '.active-preset.json') + if (!existsSync(activePath)) { + throw new Error( + `Missing ${activePath} (which preset last built circuits/bin is unknown).\n` + + ` If dist/circuits/${preset}/ is already built, hydrate bin in seconds:\n` + + ` pnpm build:circuits --preset ${preset} --skip-if-built --no-clean --no-clean-targets\n` + + ` Otherwise run a full compile:\n` + + ` pnpm build:circuits --preset ${preset}`, + ) + } + let active: { preset?: string } = {} + try { + active = JSON.parse(readFileSync(activePath, 'utf-8')) + } catch (err: any) { + throw new Error(`Failed to parse ${activePath}: ${err.message}`) + } + if (active.preset !== preset) { + throw new Error( + `circuits/bin was last built for preset '${active.preset ?? '(missing)'}', but this run targets '${preset}'.\n` + + ` Fast fix (reuses dist/circuits/${preset}/, no full recompile):\n` + + ` pnpm build:circuits --preset ${preset} --skip-if-built --no-clean --no-clean-targets\n` + + ` Full compile only if dist is missing or stale:\n` + + ` pnpm build:circuits --preset ${preset}`, + ) + } + console.log(` ✓ circuits/bin active preset matches '${preset}'.\n`) + } } // --------------------------------------------------------------------------- @@ -382,6 +593,28 @@ async function main() { options.noCleanTargets = true } else if (arg === '--no-clean-targets') { options.noCleanTargets = true + } else if (arg === '--check') { + options.check = true + } else if (arg === '--write') { + options.check = false + } else if (arg === '--preset') { + const value = args[++i] + if (!value || value.startsWith('--')) { + console.error('Error: --preset requires a value (insecure-512 | secure-8192)') + process.exit(1) + } + if (!ALL_PRESETS.includes(value as (typeof ALL_PRESETS)[number])) { + console.error(`Error: unknown preset '${value}'. Expected one of: ${ALL_PRESETS.join(', ')}`) + process.exit(1) + } + options.preset = value + } else if (arg === '--output-dir') { + const value = args[++i] + if (!value || value.startsWith('--')) { + console.error('Error: --output-dir requires a path') + process.exit(1) + } + options.outputDir = value } else if (arg === '--group') { const value = args[++i] if (!value || value.startsWith('--')) { @@ -407,25 +640,45 @@ function showHelp() { console.log(` Usage: generate-verifiers [options] -Generates Solidity verifier contracts from compiled Noir circuits -and places them in packages/enclave-contracts/contracts/verifiers/bfv/honk/. +Generates (or verifies) Solidity Honk verifier contracts from compiled Noir +circuits and places them in packages/enclave-contracts/contracts/verifiers/bfv/honk/. + +The Solidity verifiers are committed to git. Test and benchmark flows run +this script with --check so accidental drift between committed verifiers and +current circuit VKs is surfaced as a failure rather than a silent rewrite. Options: + --check Verify committed verifiers match current VKs (no writes). + Exits non-zero on drift. Used by test/benchmark/CI flows. + --preset BFV preset for circuits/bin (insecure-512 | secure-8192). + Defaults to insecure-512. With --check and a non-insecure preset, + only verifies dist/ + circuits/bin alignment (no .sol diff). + --output-dir Write generated verifiers here instead of the committed honk/ dir. + --write Write/overwrite committed verifiers (this is the default + when neither --check nor --write is passed). --circuits Circuit names (comma-separated). When omitted, generates all circuits. --group Circuit groups (comma-separated: dkg,threshold,recursive_aggregation) - --clean Remove existing verifier directory before generating + --clean Remove existing verifier directory before generating (write mode only). --no-compile Don't compile circuits automatically (fail if not already compiled); - also skips cleaning nargo target dirs (use after build:circuits) - --no-clean-targets Don't delete nargo target dirs before generating verifiers - --dry-run Show what would be generated without doing anything - -h, --help Show this help message + also skips cleaning nargo target dirs (use after build:circuits). + --no-clean-targets Don't delete nargo target dirs before generating verifiers. + --dry-run Show what would be generated without doing anything. + -h, --help Show this help message. Examples: - pnpm generate:verifiers --circuits dkg_aggregator,decryption_aggregator - pnpm generate:verifiers --circuits dkg_aggregator --clean + pnpm generate:verifiers # Rewrite committed verifiers (manual) + pnpm generate:verifiers --check # Verify committed verifiers (CI/tests) + pnpm generate:verifiers --circuits dkg_aggregator # Single circuit + pnpm generate:verifiers --check --no-compile # Verify against existing artifacts only `) } -if (require.main === module) main() +if (require.main === module) { + main().catch((err: unknown) => { + const msg = err instanceof Error ? err.message : String(err) + console.error(`\n❌ ${msg}\n`) + process.exit(1) + }) +} export { VerifierGenerator, GenerateOptions, CircuitGroup, CIRCUIT_GROUPS } diff --git a/templates/default/contracts/Mocks/MockRISC0Verifier.sol b/templates/default/contracts/Mocks/MockRISC0Verifier.sol index 4505735869..566ff34694 100644 --- a/templates/default/contracts/Mocks/MockRISC0Verifier.sol +++ b/templates/default/contracts/Mocks/MockRISC0Verifier.sol @@ -3,12 +3,12 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -pragma solidity ^0.8.27; +pragma solidity 0.8.28; -import { IRiscZeroVerifier, Receipt } from "@risc0/ethereum/contracts/IRiscZeroVerifier.sol"; +import { IRiscZeroVerifier, Receipt } from "risc0/IRiscZeroVerifier.sol"; contract MockRISC0Verifier is IRiscZeroVerifier { - function verify(bytes calldata seal, bytes32 imageId, bytes32 journalDigest) public view override {} + function verify(bytes calldata seal, bytes32 imageId, bytes32 journalDigest) external view override {} function verifyIntegrity(Receipt calldata receipt) external view override {} } diff --git a/templates/default/contracts/MyProgram.sol b/templates/default/contracts/MyProgram.sol index f02def5b41..b33ffaa5d4 100755 --- a/templates/default/contracts/MyProgram.sol +++ b/templates/default/contracts/MyProgram.sol @@ -3,9 +3,9 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -pragma solidity >=0.8.27; +pragma solidity 0.8.28; -import { IRiscZeroVerifier } from "@risc0/ethereum/contracts/IRiscZeroVerifier.sol"; +import { IRiscZeroVerifier } from "risc0/IRiscZeroVerifier.sol"; import { IE3Program } from "@enclave-e3/contracts/contracts/interfaces/IE3Program.sol"; import { IEnclave } from "@enclave-e3/contracts/contracts/interfaces/IEnclave.sol"; import { E3 } from "@enclave-e3/contracts/contracts/interfaces/IE3.sol"; @@ -53,9 +53,7 @@ contract MyProgram is IE3Program, Ownable { authorizedContracts[address(_enclave)] = true; } - /// @notice Validate the E3 program parameters - /// @param e3Id The E3 program ID - /// @param e3ProgramParams The E3 program parameters + /// @inheritdoc IE3Program function validate(uint256 e3Id, uint256, bytes calldata e3ProgramParams, bytes calldata, bytes calldata) external returns (bytes32) { require(authorizedContracts[msg.sender] || msg.sender == owner(), CallerNotAuthorized()); require(paramsHashes[e3Id] == bytes32(0), E3AlreadyInitialized()); @@ -91,7 +89,7 @@ contract MyProgram is IE3Program, Ownable { /// @param e3Id The E3 program ID /// @param ciphertextOutputHash The hash of the ciphertext output /// @param proof The proof to verify - function verify(uint256 e3Id, bytes32 ciphertextOutputHash, bytes memory proof) external view override returns (bool) { + function verify(uint256 e3Id, bytes32 ciphertextOutputHash, bytes memory proof) external override returns (bool) { require(paramsHashes[e3Id] != bytes32(0), E3DoesNotExist()); bytes32 inputRoot = bytes32(inputs[e3Id]._root()); bytes memory journal = new bytes(396); // (32 + 1) * 4 * 3 diff --git a/templates/default/deploy/default.ts b/templates/default/deploy/default.ts index 7805afe11f..b1c7747c7a 100644 --- a/templates/default/deploy/default.ts +++ b/templates/default/deploy/default.ts @@ -4,12 +4,11 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -import { readDeploymentArgs, storeDeploymentArgs, updateE3Config } from '@enclave-e3/contracts/scripts' +import { getDeploymentChain, readDeploymentArgs, storeDeploymentArgs, updateE3Config } from '@enclave-e3/contracts/scripts' import { Enclave__factory as EnclaveFactory } from '@enclave-e3/contracts/types' +import { ensureTemplateCwd, ENCLAVE_CONFIG_FILE } from '../scripts/template-paths' import { MyProgram__factory as MyProgramFactory } from '../types/factories/contracts' import hre from 'hardhat' -import path from 'path' -import { fileURLToPath } from 'url' // Map contract names to config keys const contractMapping: Record = { @@ -20,15 +19,12 @@ const contractMapping: Record = { MockUSDC: 'fee_token', } -// Get __dirname equivalent in ES modules -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - export const deployTemplate = async () => { + ensureTemplateCwd() const { ethers } = await hre.network.connect() const [owner] = await ethers.getSigners() - const chain = hre.globalOptions.network + const chain = getDeploymentChain(hre) const enclaveAddress = readDeploymentArgs('Enclave', chain)?.address if (!enclaveAddress) { @@ -68,10 +64,15 @@ export const deployTemplate = async () => { const e3Program = await e3ProgramFactory.deploy(await enclave.getAddress(), await verifier.getAddress(), programId) await e3Program.waitForDeployment() - const tx = await enclave.enableE3Program(await e3Program.getAddress()) - + const programAddress = await e3Program.getAddress() + const tx = await enclave.enableE3Program(programAddress) await tx.wait() + const allowed = await enclave.e3Programs(programAddress) + if (!allowed) { + throw new Error(`MyProgram ${programAddress} was not enabled on Enclave ${enclaveAddress}`) + } + console.log("E3 Program enabled for Enclave's template") console.log( @@ -90,6 +91,5 @@ export const deployTemplate = async () => { chain, ) - // this expects you to run it from CRISP's root - updateE3Config(chain, path.join(__dirname, '..', 'enclave.config.yaml'), contractMapping) + updateE3Config(chain, ENCLAVE_CONFIG_FILE, contractMapping) } diff --git a/templates/default/deployed_contracts.json b/templates/default/deployed_contracts.json index 919a10deee..2bb29ced8a 100644 --- a/templates/default/deployed_contracts.json +++ b/templates/default/deployed_contracts.json @@ -1,21 +1,21 @@ { "localhost": { "PoseidonT3": { - "blockNumber": 6, + "blockNumber": 17, "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "MockUSDC": { "constructorArgs": { "initialSupply": "1000000" }, - "blockNumber": 7, + "blockNumber": 18, "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" }, "EnclaveToken": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 8, + "blockNumber": 19, "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" }, "EnclaveTicketToken": { @@ -24,7 +24,7 @@ "registry": "0x0000000000000000000000000000000000000001", "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 10, + "blockNumber": 21, "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" }, "SlashingManager": { @@ -89,8 +89,8 @@ "proxyAdminAddress": "0x1F708C24a0D3A740cD47cC0444E9480899f3dA7D", "implementationAddress": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, - "blockNumber": 17, - "address": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" + "blockNumber": 27, + "address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, "E3RefundManager": { "constructorArgs": { @@ -105,32 +105,52 @@ "proxyAdminAddress": "0x8e80FFe6Dc044F4A766Afd6e5a8732Fe0977A493", "implementationAddress": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" }, - "blockNumber": 19, - "address": "0x9A676e781A523b5d0C0e43731313A708CB607508" + "blockNumber": 29, + "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" }, "MockComputeProvider": { - "blockNumber": 22, - "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" + "blockNumber": 31, + "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" }, "MockDecryptionVerifier": { - "blockNumber": 23, - "address": "0x851356ae760d987E095750cCeb3bC6014560891C" + "blockNumber": 32, + "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9" }, "MockPkVerifier": { - "blockNumber": 24, - "address": "0xf5059a5D33d5853360D16C683c16e67980206f36" + "blockNumber": 33, + "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" }, "MockE3Program": { - "blockNumber": 25, + "blockNumber": 34, + "address": "0x851356ae760d987E095750cCeb3bC6014560891C" + }, + "ZKTranscriptLib": { + "blockNumber": 36, "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778" }, + "DecryptionAggregatorVerifier": { + "blockNumber": 37, + "address": "0x998abeb3E57409262aE5b751f60747921B33613E" + }, + "DkgAggregatorVerifier": { + "blockNumber": 38, + "address": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49" + }, + "BfvDecryptionVerifier": { + "blockNumber": 39, + "address": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528" + }, + "BfvPkVerifier": { + "blockNumber": 41, + "address": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF" + }, "ImageID": { - "address": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF", - "blockNumber": 30 + "address": "0x5eb3Bc0a489C5A8288765d2336659EbCA68FCd00", + "blockNumber": 44 }, "MyProgram": { - "address": "0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf", - "blockNumber": 32 + "address": "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570", + "blockNumber": 46 } } } \ No newline at end of file diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index 3129d7053a..3b92a5ae62 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -6,8 +6,8 @@ chains: address: "0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf" deploy_block: 32 enclave: - address: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" - deploy_block: 17 + address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" + deploy_block: 16 ciphernode_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" deploy_block: 12 @@ -19,9 +19,18 @@ chains: deploy_block: 11 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - deploy_block: 7 + deploy_block: 6 + e3_program: + address: "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF" + deploy_block: 51 program: dev: true +# Default profile: multithread uses (logical CPUs - 1) Rayon workers and the same concurrent job +# limit. Reserve threads with `multithread_reserve_threads` (default 1 for Actix / libp2p). +# Example override on a 16-core host: +# node: +# multithread_reserve_threads: 1 +# multithread_concurrent_jobs: 8 nodes: cn1: address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" diff --git a/templates/default/hardhat.config.ts b/templates/default/hardhat.config.ts index 1161e06f6c..666da37a44 100644 --- a/templates/default/hardhat.config.ts +++ b/templates/default/hardhat.config.ts @@ -83,6 +83,16 @@ const config: HardhatUserConfig = { type: 'edr-simulated', chainType: 'l1', }, + localhost: { + accounts: { + mnemonic, + }, + chainId: chainIds.hardhat, + url: 'http://localhost:8545', + type: 'http', + chainType: 'l1', + timeout: 60000, + }, ganache: { accounts: { mnemonic, diff --git a/templates/default/package.json b/templates/default/package.json index 9f6d90b9bf..f7d987c0e9 100644 --- a/templates/default/package.json +++ b/templates/default/package.json @@ -15,7 +15,7 @@ "dev:frontend": "./scripts/dev_frontend.sh", "dev:program": "./scripts/dev_program.sh", "dev:server": "./scripts/dev_server.sh", - "predev:all": "[ ! -f './contracts/ImageID.sol' ] && enclave program compile || true", + "predev:all": "[ ! -f './.enclave/generated/contracts/ImageID.sol' ] && enclave program compile || true", "test": "hardhat test", "test:integration": "./scripts/test_integration.sh" }, diff --git a/templates/default/remappings.txt b/templates/default/remappings.txt new file mode 100644 index 0000000000..f9ea5eab6e --- /dev/null +++ b/templates/default/remappings.txt @@ -0,0 +1,5 @@ +risc0/=lib/risc0-ethereum/contracts/src/ +@enclave-e3/contracts/=node_modules/@enclave-e3/contracts/ +@zk-kit/lazy-imt.sol/=node_modules/@zk-kit/lazy-imt.sol/ +poseidon-solidity/=node_modules/poseidon-solidity/ +@openzeppelin/=node_modules/@openzeppelin/ diff --git a/templates/default/scripts/anvil-automine.mjs b/templates/default/scripts/anvil-automine.mjs new file mode 100644 index 0000000000..36eb9e7748 --- /dev/null +++ b/templates/default/scripts/anvil-automine.mjs @@ -0,0 +1,42 @@ +// Keeps anvil block.timestamp moving during local integration (finalizeCommittee needs +// block.timestamp > committeeDeadline; eth_call-only retries do not mine blocks). + +const RPC = process.env.ANVIL_RPC_URL ?? 'http://127.0.0.1:8545' + +async function rpc(method, params = []) { + const res = await fetch(RPC, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }), + }) + if (!res.ok) { + throw new Error(`RPC ${method} HTTP ${res.status}`) + } + const json = await res.json() + if (json.error) { + throw new Error(json.error.message ?? JSON.stringify(json.error)) + } +} + +let failureCount = 0 +let lastLoggedTime = 0 +const LOG_INTERVAL_MS = 30_000 + +async function loop() { + for (;;) { + try { + await rpc('evm_mine') + failureCount = 0 + } catch (err) { + failureCount++ + const now = Date.now() + if (failureCount === 1 || now - lastLoggedTime >= LOG_INTERVAL_MS) { + console.error(`[anvil-automine] evm_mine failed (attempt ${failureCount}):`, err) + lastLoggedTime = now + } + } + await new Promise((r) => setTimeout(r, 1000)) + } +} + +loop() diff --git a/templates/default/scripts/deploy-local.ts b/templates/default/scripts/deploy-local.ts index b195bb1328..059d275585 100644 --- a/templates/default/scripts/deploy-local.ts +++ b/templates/default/scripts/deploy-local.ts @@ -6,8 +6,10 @@ import { deployEnclave } from '@enclave-e3/contracts/scripts' import { deployTemplate } from '../deploy/default' +import { ensureTemplateCwd } from './template-paths' async function main() { + ensureTemplateCwd() console.log('🚀 Deploying Enclave protocol locally...') // Get hardhat runtime environment @@ -20,10 +22,12 @@ async function main() { console.log('Deploying with account:', deployer.address) console.log('Account balance:', ethers.formatEther(await ethers.provider.getBalance(deployer.address))) - // Execute the deployment + // Mocks for local dev; skip on-chain ZK verifiers (needs pnpm compile:circuits). await deployEnclave(true, false) await deployTemplate() } -// Execute the deployment -main().catch(console.error) +main().catch((err) => { + console.error(err) + process.exit(1) +}) diff --git a/templates/default/scripts/dev_ciphernodes.sh b/templates/default/scripts/dev_ciphernodes.sh index 37133c1dab..62e5e196ae 100755 --- a/templates/default/scripts/dev_ciphernodes.sh +++ b/templates/default/scripts/dev_ciphernodes.sh @@ -2,6 +2,8 @@ set -euo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")/.." + SIGNAL_FILE=/tmp/enclave_ciphernodes_ready cleanup() { @@ -20,7 +22,12 @@ trap cleanup INT TERM echo "Waiting for local evm node..." pnpm wait-on tcp:localhost:8545 -# nuke past installations as we are adding these nodes to the contract +if [ ! -f './.enclave/generated/contracts/ImageID.sol' ]; then + echo "Compiling guest program (ImageID)..." + enclave program compile +fi + +# Fresh node state for this deploy rm -rf .enclave/data rm -rf .enclave/config @@ -39,10 +46,14 @@ enclave wallet set --name cn5 --private-key "$PRIVATE_KEY_CN5" echo "Setting up ZK prover..." enclave noir setup -# using & instead of -d so that wait works below -enclave nodes up -v & - -sleep 2 +# Deploy before starting nodes so enclave.config.yaml addresses match the chain. +echo "Deploying protocol + MyProgram..." +pnpm exec hardhat utils:clean-deployments --network localhost +pnpm exec hardhat run scripts/deploy-local.ts --network localhost +if ! grep -q '"MyProgram"' deployed_contracts.json; then + echo "deployTemplate did not record MyProgram — check deploy logs above" + exit 1 +fi CN1=$(grep -A 1 'cn1:' enclave.config.yaml | grep 'address:' | sed 's/.*address: *"\([^"]*\)".*/\1/') CN2=$(grep -A 1 'cn2:' enclave.config.yaml | grep 'address:' | sed 's/.*address: *"\([^"]*\)".*/\1/') @@ -50,8 +61,10 @@ CN3=$(grep -A 1 'cn3:' enclave.config.yaml | grep 'address:' | sed 's/.*address: CN4=$(grep -A 1 'cn4:' enclave.config.yaml | grep 'address:' | sed 's/.*address: *"\([^"]*\)".*/\1/') CN5=$(grep -A 1 'cn5:' enclave.config.yaml | grep 'address:' | sed 's/.*address: *"\([^"]*\)".*/\1/') -# Add ciphernodes using variables from config.sh -pnpm run deploy && sleep 2 +echo "Starting ciphernodes (post-deploy config)..." +enclave nodes up -v & + +sleep 4 pnpm hardhat ciphernode:admin-add --ciphernode-address $CN1 --network localhost pnpm hardhat ciphernode:admin-add --ciphernode-address $CN2 --network localhost diff --git a/templates/default/scripts/template-paths.ts b/templates/default/scripts/template-paths.ts new file mode 100644 index 0000000000..7d7f7a76bc --- /dev/null +++ b/templates/default/scripts/template-paths.ts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// Keeps Hardhat deploy scripts scoped to this template directory only. + +import path from 'path' +import { fileURLToPath } from 'url' + +const scriptsDir = path.dirname(fileURLToPath(import.meta.url)) + +/** Absolute path to `templates/default`. */ +export const TEMPLATE_ROOT = path.resolve(scriptsDir, '..') + +export const DEPLOYMENTS_FILE = path.join(TEMPLATE_ROOT, 'deployed_contracts.json') +export const ENCLAVE_CONFIG_FILE = path.join(TEMPLATE_ROOT, 'enclave.config.yaml') + +/** Pin cwd so `@enclave-e3/contracts` deployment helpers write only under the template. */ +export function ensureTemplateCwd(): void { + if (process.cwd() !== TEMPLATE_ROOT) { + process.chdir(TEMPLATE_ROOT) + } +} diff --git a/templates/default/scripts/test_integration.sh b/templates/default/scripts/test_integration.sh index 171f776cde..6622e3bdd7 100755 --- a/templates/default/scripts/test_integration.sh +++ b/templates/default/scripts/test_integration.sh @@ -2,6 +2,8 @@ set -euo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")/.." + passed_message() { echo "" echo "------------------------" @@ -19,14 +21,14 @@ failed_message() { exit 1 } -export $(enclave print-env --chain localhost) (pnpm concurrently \ - --names "TEST,EVM,CIPHER,SERVER,PROGRAM" \ - --prefix-colors "blue,cyan,magenta,yellow,green" \ + --names "TEST,EVM,MINE,CIPHER,SERVER,PROGRAM" \ + --prefix-colors "blue,cyan,gray,magenta,yellow,green" \ --kill-others \ --success first \ - "wait-on http://localhost:13151/health && pnpm vitest run ./tests/integration.spec.ts" \ - "anvil --host 0.0.0.0 --chain-id 31337 --block-time 1 --mnemonic 'test test test test test test test test test test test junk' --silent" \ + "wait-on file:/tmp/enclave_ciphernodes_ready tcp:localhost:8545 http://localhost:13151/health && export \$(enclave print-env --chain localhost) && pnpm vitest run ./tests/integration.spec.ts" \ + "anvil --host 0.0.0.0 --chain-id 31337 --mnemonic 'test test test test test test test test test test test junk' --silent" \ + "wait-on tcp:localhost:8545 && node ./scripts/anvil-automine.mjs" \ "pnpm dev:ciphernodes" \ "TEST_MODE=1 pnpm dev:server" \ "pnpm dev:program" && passed_message) || failed_message diff --git a/templates/default/tests/anvil-helpers.ts b/templates/default/tests/anvil-helpers.ts new file mode 100644 index 0000000000..64c7cb42df --- /dev/null +++ b/templates/default/tests/anvil-helpers.ts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import type { PublicClient } from 'viem' + +/** Advance anvil time and mine (closes sortition submission window on-chain). */ +export async function advanceAnvilTime(publicClient: PublicClient, seconds: number): Promise { + await publicClient.request({ + method: 'evm_increaseTime', + params: [seconds], + }) + await publicClient.request({ + method: 'evm_mine', + params: [], + }) +} + +export function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)) +} diff --git a/templates/default/tests/integration.spec.ts b/templates/default/tests/integration.spec.ts index dd4c735367..6001b97926 100644 --- a/templates/default/tests/integration.spec.ts +++ b/templates/default/tests/integration.spec.ts @@ -25,6 +25,8 @@ import { publishInput } from '../server/input' import { privateKeyToAccount } from 'viem/accounts' import { anvil } from 'viem/chains' +import { advanceAnvilTime, sleep } from './anvil-helpers' + export function getContractAddresses() { return { enclave: process.env.ENCLAVE_ADDRESS as `0x${string}`, @@ -189,7 +191,8 @@ describe('Integration', () => { const { waitForEvent } = await setupEventListeners(sdk, store) const committeeSize = CommitteeSize.Micro - const duration = 1000 + // Input window length (seconds); also used as vitest wait budget per phase. + const duration = 120 const inputWindow = await calculateInputWindow(publicClient, duration) const computeProviderParams = encodeComputeProviderParams( DEFAULT_COMPUTE_PROVIDER_PARAMS, @@ -217,7 +220,7 @@ describe('Integration', () => { const hash = await sdk.approveFeeToken(quote) console.log('Fee token approved:', hash) - await new Promise((resolve) => setTimeout(resolve, 1000)) + await sleep(1000) // REQUEST phase const timeoutMs = duration * 1000 @@ -241,7 +244,15 @@ describe('Integration', () => { const stageAfterRequest = await sdk.getE3Stage(state.e3Id) assert.strictEqual(stageAfterRequest, E3Stage.Requested, 'E3 stage should be Requested after requestE3') + // Ciphernodes submit tickets during the on-chain sortition window (10s). Anvil must mine so + // block.timestamp passes committeeDeadline before finalizeCommittee (see anvil-automine.mjs). + console.log('Waiting for ciphernode sortition tickets...') + await sleep(8000) + await advanceAnvilTime(publicClient, 15) + console.log('Advanced anvil past sortition deadline') + // Ciphernodes will publish a public key within the COMMITTEE_PUBLISHED event + console.log('Waiting for committee + DKG (CommitteePublished)...') event = await waitForEvent(RegistryEventType.COMMITTEE_PUBLISHED, undefined, timeoutMs) const publicKeyBytes = hexToBytes(event.data.publicKey as `0x${string}`) diff --git a/tests/integration/base.sh b/tests/integration/base.sh index 89450ca2c6..526174b92a 100755 --- a/tests/integration/base.sh +++ b/tests/integration/base.sh @@ -18,7 +18,17 @@ until curl -sf -X POST http://localhost:8545 -H 'Content-Type: application/json' done pnpm evm:clean -pnpm evm:deploy --network localhost + +if [[ "$PROOF_AGGREGATION_ENABLED" == "true" ]]; then + heading "Deploy contracts (ZK verification + fold attestation verifier)" + ENABLE_ZK_VERIFICATION=true pnpm evm:deploy +else + heading "Deploy contracts (mock verifiers)" + pnpm evm:deploy +fi + +heading "Sync tests/integration/enclave.config.yaml from deployed_contracts.json" +(cd "$ROOT_DIR/packages/enclave-contracts" && pnpm utils:sync-integration-config) enclave_wallet_set cn1 "$PRIVATE_KEY_CN1" enclave_wallet_set cn2 "$PRIVATE_KEY_CN2" @@ -26,7 +36,7 @@ enclave_wallet_set cn3 "$PRIVATE_KEY_CN3" enclave_wallet_set cn4 "$PRIVATE_KEY_CN4" enclave_wallet_set cn5 "$PRIVATE_KEY_CN5" -heading "Setup ZK prover" +heading "Setup ZK prover (bb binary; circuits staged in prebuild when proof aggregation is on)" $ENCLAVE_BIN noir setup # start swarm @@ -53,7 +63,7 @@ pnpm ciphernode:add --ciphernode-address $CIPHERNODE_ADDRESS_4 --network localho heading "Add ciphernode $CIPHERNODE_ADDRESS_5" pnpm ciphernode:add --ciphernode-address $CIPHERNODE_ADDRESS_5 --network localhost -heading "Request Committee" +heading "Request Committee (proof-aggregation-enabled=$PROOF_AGGREGATION_ENABLED)" ENCODED_PARAMS=0x$($SCRIPT_DIR/lib/pack_e3_params.sh \ --moduli 0xffffee001 \ @@ -73,29 +83,50 @@ pnpm committee:new \ --input-window-end "$INPUT_WINDOW_END" \ --e3-params "$ENCODED_PARAMS" \ --committee-size 0 \ - --proof-aggregation-enabled false + --proof-aggregation-enabled "$PROOF_AGGREGATION_ENABLED" -wait_for_committee_pubkey 0 "$SCRIPT_DIR/output/pubkey.bin" +wait_for_committee_pubkey 0 "$SCRIPT_DIR/output/pubkey.bin" "$INTEGRATION_DKG_TIMEOUT" + +if [[ "$PROOF_AGGREGATION_ENABLED" == "true" ]]; then + heading "Verify active aggregator (proof aggregation / DKG path)" + ACTIVE_AGG_ADDRESS=$(wait_for_active_aggregator_address 0 "$INTEGRATION_DKG_TIMEOUT") + echo "Active aggregator: $ACTIVE_AGG_ADDRESS" +fi heading "Query events via daemon REST API" daemon_query_events cn1 "$SCRIPT_DIR/output/events.txt" check_last_line "$SCRIPT_DIR/output/events.txt" '{"Next":10}' -heading "Mock encrypted plaintext" -$SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext $PLAINTEXT --params "$ENCODED_PARAMS" +if [[ "$PROOF_AGGREGATION_ENABLED" == "true" ]]; then + heading "Wire MockE3Program → Enclave so publishInput triggers decryption" + pnpm e3-program:setMockEnclave --network localhost -heading "Mock publish input e3-id" -pnpm e3-program:publishInput --network localhost --e3-id 0 --data 0x12345678 + heading "Encrypt plaintext under the published committee pubkey" + $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext "$PLAINTEXT" --params "$ENCODED_PARAMS" + waiton "$SCRIPT_DIR/output/output.bin" -sleep 4 + heading "Publish E3 input (forwards to publishCiphertextOutput; nodes run decryption with ZK proofs)" + pnpm e3-program:publishInput --network localhost --e3-id 0 --data-file "$SCRIPT_DIR/output/output.bin" -waiton "$SCRIPT_DIR/output/output.bin" + heading "Wait for on-chain plaintext (BFV decryption verifier)" + wait_for_plaintext_output 0 "$SCRIPT_DIR/output/plaintext.txt" "$INTEGRATION_DKG_TIMEOUT" +else + heading "Mock encrypted plaintext" + $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext "$PLAINTEXT" --params "$ENCODED_PARAMS" -heading "Publish ciphertext to EVM" -pnpm e3:publishCiphertext --e3-id 0 --network localhost --data-file "$SCRIPT_DIR/output/output.bin" --proof 0x12345678 + heading "Mock publish input e3-id" + pnpm e3-program:publishInput --network localhost --e3-id 0 --data 0x12345678 -wait_for_plaintext_output 0 "$SCRIPT_DIR/output/plaintext.txt" + sleep 4 + + waiton "$SCRIPT_DIR/output/output.bin" + + heading "Publish ciphertext to EVM" + pnpm e3:publishCiphertext --e3-id 0 --network localhost --data-file "$SCRIPT_DIR/output/output.bin" --proof 0x12345678 + + wait_for_plaintext_output 0 "$SCRIPT_DIR/output/plaintext.txt" +fi ACTUAL=$(cut -d',' -f1,2 $SCRIPT_DIR/output/plaintext.txt) diff --git a/tests/integration/enclave.config.yaml b/tests/integration/enclave.config.yaml index 06fd9b20c9..e93ecf649d 100644 --- a/tests/integration/enclave.config.yaml +++ b/tests/integration/enclave.config.yaml @@ -1,25 +1,26 @@ +# Sync from packages/enclave-contracts/deployed_contracts.json (localhost) after each `pnpm evm:deploy`. chains: - name: "localhost" rpc_url: "ws://localhost:8545" contracts: e3_program: - address: "0x95401dc811bb5740090279Ba06cfA8fcF6113778" - deploy_block: 1 # Set to actual deploy block + address: "0x851356ae760d987E095750cCeb3bC6014560891C" + deploy_block: 23 enclave: - address: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" - deploy_block: 1 # Set to actual deploy block + address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" + deploy_block: 16 ciphernode_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" - deploy_block: 1 # Set to actual deploy block + deploy_block: 12 bonding_registry: address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" - deploy_block: 1 # Set to actual deploy block + deploy_block: 13 slashing_manager: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" - deploy_block: 1 # Set to actual deploy block + deploy_block: 11 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - deploy_block: 1 # Set to actual deploy block + deploy_block: 7 program: dev: true diff --git a/tests/integration/fns.sh b/tests/integration/fns.sh index 76afc0029a..9332d11a7a 100644 --- a/tests/integration/fns.sh +++ b/tests/integration/fns.sh @@ -5,6 +5,10 @@ set -euo pipefail # Stricter error handling SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" PLAINTEXT="4,0" + +# Set by test.sh via export_integration_flags +PROOF_AGGREGATION_ENABLED="${PROOF_AGGREGATION_ENABLED:-false}" +INTEGRATION_DKG_TIMEOUT="${INTEGRATION_DKG_TIMEOUT:-1300}" ID=$(date +%s) if [[ "$SCRIPT_DIR" != "$(pwd)" ]]; then @@ -101,7 +105,7 @@ waiton-files() { wait_for_committee_pubkey() { local e3_id="$1" local out_file="$2" - local timeout="${3:-1300}" + local timeout="${3:-$INTEGRATION_DKG_TIMEOUT}" local start_time=$(date +%s) while true; do diff --git a/tests/integration/lib/prebuild.sh b/tests/integration/lib/prebuild.sh index 84aefd6e7d..9571934e76 100755 --- a/tests/integration/lib/prebuild.sh +++ b/tests/integration/lib/prebuild.sh @@ -1,9 +1,54 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash set -eu # Exit immediately if a command exits with a non-zero status + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)" +INTEGRATION_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +INTEGRATION_NOIR="${INTEGRATION_DIR}/.enclave/noir" +VERSIONS_JSON="${ROOT_DIR}/crates/zk-prover/versions.json" + echo "" echo "PREBUILDING BINARIES..." echo "" -cd ../../crates && cargo build --bin fake_encrypt --bin pack_e3_params; +(cd "$ROOT_DIR/crates" && cargo build --bin fake_encrypt --bin pack_e3_params) echo "" echo "FINISHED PREBUILDING BINARIES" echo "" + +if [[ "${PROOF_AGGREGATION_ENABLED:-false}" == "true" ]]; then + echo "" + echo "BUILDING ZK CIRCUITS + ON-CHAIN VERIFIERS (proof aggregation enabled)..." + echo "" + + # Nodes must prove with the same VKs as deployed Honk verifiers. `enclave noir setup` + # otherwise installs the release tarball (crates/zk-prover/versions.json), which can + # disagree with locally rebuilt circuits/verifiers after `build:circuits`. + rm -rf "${INTEGRATION_NOIR}/circuits" + mkdir -p "${INTEGRATION_NOIR}/circuits" + + (cd "$ROOT_DIR" && pnpm build:circuits --preset insecure-512 -o "${INTEGRATION_NOIR}/circuits") + # `--check`: verify the committed Honk Solidity verifiers in + # packages/enclave-contracts/contracts/verifiers/bfv/honk/ match the + # freshly-built circuits' recursive VKs. Fails loudly on drift instead of + # silently rewriting committed contracts mid-test. If this errors, run + # `pnpm generate:verifiers --write` and commit the diff. + (cd "$ROOT_DIR" && pnpm generate:verifiers --check --no-compile --no-clean-targets) + + if ! command -v jq >/dev/null 2>&1; then + echo "jq is required to pin noir/version.json for integration ZK fixtures" >&2 + exit 1 + fi + REQUIRED_BB="$(jq -r '.required_bb_version' "$VERSIONS_JSON")" + REQUIRED_CIRCUITS="$(jq -r '.required_circuits_version' "$VERSIONS_JSON")" + jq -n \ + --arg bb "$REQUIRED_BB" \ + --arg circuits "$REQUIRED_CIRCUITS" \ + '{bb_version: $bb, circuits_version: $circuits}' \ + > "${INTEGRATION_NOIR}/version.json" + + echo "Staged circuits under ${INTEGRATION_NOIR}/circuits/insecure-512" + echo "Pinned noir version.json (bb=${REQUIRED_BB}, circuits=${REQUIRED_CIRCUITS})" + echo "" + echo "FINISHED BUILDING ZK CIRCUITS + VERIFIERS" + echo "" +fi diff --git a/tests/integration/output/.gitignore b/tests/integration/output/.gitignore index 9361043402..d881a74856 100644 --- a/tests/integration/output/.gitignore +++ b/tests/integration/output/.gitignore @@ -2,3 +2,4 @@ plaintext.txt *.hex *.db +events.txt diff --git a/tests/integration/persist.sh b/tests/integration/persist.sh index 8c7445dfca..791b8a8384 100755 --- a/tests/integration/persist.sh +++ b/tests/integration/persist.sh @@ -18,7 +18,14 @@ until curl -sf -X POST http://localhost:8545 -H 'Content-Type: application/json' done pnpm evm:clean -pnpm evm:deploy --network localhost + +if [[ "${PROOF_AGGREGATION_ENABLED:-false}" == "true" ]]; then + ENABLE_ZK_VERIFICATION=true pnpm evm:deploy +else + pnpm evm:deploy +fi + +(cd "$ROOT_DIR/packages/enclave-contracts" && pnpm utils:sync-integration-config) enclave_wallet_set cn1 "$PRIVATE_KEY_CN1" enclave_wallet_set cn2 "$PRIVATE_KEY_CN2" @@ -67,9 +74,9 @@ pnpm committee:new \ --input-window-end "$INPUT_WINDOW_END" \ --e3-params "$ENCODED_PARAMS" \ --committee-size 0 \ - --proof-aggregation-enabled false + --proof-aggregation-enabled "${PROOF_AGGREGATION_ENABLED:-false}" -wait_for_committee_pubkey 0 "$SCRIPT_DIR/output/pubkey.bin" +wait_for_committee_pubkey 0 "$SCRIPT_DIR/output/pubkey.bin" "${INTEGRATION_DKG_TIMEOUT:-1300}" ACTIVE_AGG_ADDRESS=$(wait_for_active_aggregator_address 0) if ! ACTIVE_AGG=$(node_name_for_address "$ACTIVE_AGG_ADDRESS"); then @@ -93,7 +100,7 @@ enclave_nodes_start "$ACTIVE_AGG" sleep 5 heading "Mock encrypted plaintext" -$SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext $PLAINTEXT --params "$ENCODED_PARAMS" +$SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext "$PLAINTEXT" --params "$ENCODED_PARAMS" heading "Mock publish input e3-id" pnpm e3-program:publishInput --network localhost --e3-id 0 --data 0x12345678 diff --git a/tests/integration/test.sh b/tests/integration/test.sh index 1ce5442367..f8bde2cc02 100755 --- a/tests/integration/test.sh +++ b/tests/integration/test.sh @@ -4,16 +4,58 @@ set -eu # Exit immediately if a command exits with a non-zero status THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -if [[ "$*" != *"--no-prebuild"* ]]; then - "$THIS_DIR/lib/prebuild.sh" -fi +PROOF_AGGREGATION_ENABLED="${PROOF_AGGREGATION_ENABLED:-false}" +SKIP_PREBUILD=false + +parse_integration_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + --proof-aggregation-enabled) + shift + PROOF_AGGREGATION_ENABLED="${1:-true}" + shift + ;; + --no-prebuild) + SKIP_PREBUILD=true + shift + ;; + *) + echo "Unknown integration argument: $1" >&2 + echo "Usage: ./test.sh [base|persist|net|restart] [--proof-aggregation-enabled true|false] [--no-prebuild]" >&2 + exit 1 + ;; + esac + done +} + +export_integration_flags() { + export PROOF_AGGREGATION_ENABLED + if [[ "$PROOF_AGGREGATION_ENABLED" == "true" ]]; then + export ENABLE_ZK_VERIFICATION=true + export INTEGRATION_DKG_TIMEOUT="${INTEGRATION_DKG_TIMEOUT:-3600}" + else + export ENABLE_ZK_VERIFICATION=false + export INTEGRATION_DKG_TIMEOUT="${INTEGRATION_DKG_TIMEOUT:-1300}" + fi +} -if [ $# -eq 0 ]; then +if [ $# -eq 0 ]; then + export PROOF_AGGREGATION_ENABLED=false + export_integration_flags + "$THIS_DIR/lib/prebuild.sh" "$THIS_DIR/persist.sh" "$THIS_DIR/base.sh" "$THIS_DIR/net.sh" "$THIS_DIR/restart.sh" else - "$THIS_DIR/$1.sh" -fi + SCRIPT_NAME="$1" + shift + parse_integration_args "$@" + export_integration_flags + + if [[ "$SKIP_PREBUILD" != "true" ]]; then + "$THIS_DIR/lib/prebuild.sh" + fi + "$THIS_DIR/${SCRIPT_NAME}.sh" +fi