From 7a47ea3dedd11b9ebc447703e803bfaa1cc82d14 Mon Sep 17 00:00:00 2001 From: Sasha <33594434+sashaaldrick@users.noreply.github.com> Date: Wed, 25 Sep 2024 23:38:50 +0100 Subject: [PATCH] Added governance example to `release-1.1` (#248) Update governance example Cargo.toml + examples README for release-1.1 --------- Co-authored-by: Angelo Capossele Co-authored-by: Victor Graf --- examples/README.md | 5 + examples/governance/.gitignore | 26 + examples/governance/Cargo.lock | 5107 +++++++++++++++++ examples/governance/Cargo.toml | 35 + examples/governance/README.md | 78 + examples/governance/apps/Cargo.toml | 21 + examples/governance/apps/README.md | 24 + examples/governance/apps/src/bin/publisher.rs | 140 + .../script/DeployRiscZeroGovernor.s.sol | 97 + .../contracts/src/BaselineGovernor.sol | 72 + .../contracts/src/ExtendedGovernorBase.sol | 42 + .../contracts/src/RiscZeroGovernor.sol | 204 + .../src/RiscZeroGovernorCounting.sol | 192 + .../governance/contracts/src/VoteToken.sol | 39 + .../contracts/test/BaselineGovernorTest.sol | 167 + .../contracts/test/GovernorTestBase.sol | 111 + .../contracts/test/RiscZeroGovernorTest.sol | 248 + .../benchmarks/BenchmarkGovernorsTest.sol | 160 + .../test/benchmarks/BenchmarkTestBase.sol | 140 + .../contracts/test/benchmarks/environment.yml | 60 + .../test/benchmarks/gas_data_comparison.png | Bin 0 -> 235171 bytes .../test/benchmarks/plot_gas_data.py | 87 + .../test/benchmarks/print_gas_data.py | 52 + .../test/benchmarks/requirements.txt | 18 + examples/governance/foundry.toml | 13 + examples/governance/instructions.md | 145 + examples/governance/methods/Cargo.toml | 18 + examples/governance/methods/README.md | 11 + examples/governance/methods/build.rs | 47 + examples/governance/methods/guest/Cargo.lock | 1259 ++++ examples/governance/methods/guest/Cargo.toml | 35 + .../methods/guest/src/bin/finalize_votes.rs | 155 + examples/governance/methods/src/lib.rs | 50 + examples/governance/remappings.txt | 3 + examples/governance/rust-toolchain.toml | 4 + 35 files changed, 8865 insertions(+) create mode 100644 examples/governance/.gitignore create mode 100644 examples/governance/Cargo.lock create mode 100644 examples/governance/Cargo.toml create mode 100644 examples/governance/README.md create mode 100644 examples/governance/apps/Cargo.toml create mode 100644 examples/governance/apps/README.md create mode 100644 examples/governance/apps/src/bin/publisher.rs create mode 100644 examples/governance/contracts/script/DeployRiscZeroGovernor.s.sol create mode 100644 examples/governance/contracts/src/BaselineGovernor.sol create mode 100644 examples/governance/contracts/src/ExtendedGovernorBase.sol create mode 100644 examples/governance/contracts/src/RiscZeroGovernor.sol create mode 100644 examples/governance/contracts/src/RiscZeroGovernorCounting.sol create mode 100644 examples/governance/contracts/src/VoteToken.sol create mode 100644 examples/governance/contracts/test/BaselineGovernorTest.sol create mode 100644 examples/governance/contracts/test/GovernorTestBase.sol create mode 100644 examples/governance/contracts/test/RiscZeroGovernorTest.sol create mode 100644 examples/governance/contracts/test/benchmarks/BenchmarkGovernorsTest.sol create mode 100644 examples/governance/contracts/test/benchmarks/BenchmarkTestBase.sol create mode 100644 examples/governance/contracts/test/benchmarks/environment.yml create mode 100644 examples/governance/contracts/test/benchmarks/gas_data_comparison.png create mode 100644 examples/governance/contracts/test/benchmarks/plot_gas_data.py create mode 100644 examples/governance/contracts/test/benchmarks/print_gas_data.py create mode 100644 examples/governance/contracts/test/benchmarks/requirements.txt create mode 100644 examples/governance/foundry.toml create mode 100644 examples/governance/instructions.md create mode 100644 examples/governance/methods/Cargo.toml create mode 100644 examples/governance/methods/README.md create mode 100644 examples/governance/methods/build.rs create mode 100644 examples/governance/methods/guest/Cargo.lock create mode 100644 examples/governance/methods/guest/Cargo.toml create mode 100644 examples/governance/methods/guest/src/bin/finalize_votes.rs create mode 100644 examples/governance/methods/src/lib.rs create mode 100644 examples/governance/remappings.txt create mode 100644 examples/governance/rust-toolchain.toml diff --git a/examples/README.md b/examples/README.md index 18c0e779..048c8c5d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -16,6 +16,11 @@ Explore a more advanced interaction between [Steel] and a custom Ethereum smart This example shows how the [Steel] library can be used to call multiple view functions of a contract. This example generates a proof of a [Compound] cToken's APR (Annual Percentage Rate), showcasing the potential for on-chain verification of complex financial metrics. +## [DAO Governance with Off-chain Proofs](./governance/README.md) + +This example contains a modified version of OpenZeppelin's [Governor] contract, which takes gas intensive signature verification off-chain whilst maintaining the same trust assumptions. This contract leverages RISC Zero as a [coprocessor] for generating and verifying these proofs. + [coprocessor]: https://www.risczero.com/news/a-guide-to-zk-coprocessors-for-scalability +[Governor]: https://docs.openzeppelin.com/contracts/4.x/api/governance#governor [Steel]: ../steel [Compound]: https://compound.finance/ diff --git a/examples/governance/.gitignore b/examples/governance/.gitignore new file mode 100644 index 00000000..9b417cdb --- /dev/null +++ b/examples/governance/.gitignore @@ -0,0 +1,26 @@ +# autogenerated test data +contracts/test/benchmarks/gas_data.csv + +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/*/11155111/ +/broadcast/**/dry-run/ + +# Ignores anvil logs +anvil_logs.txt + +# Autogenerated contracts +contracts/src/ImageID.sol +contracts/src/Elf.sol + +# Cargo +target/ + +# Misc +.DS_Store +.idea \ No newline at end of file diff --git a/examples/governance/Cargo.lock b/examples/governance/Cargo.lock new file mode 100644 index 00000000..2194bd3d --- /dev/null +++ b/examples/governance/Cargo.lock @@ -0,0 +1,5107 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "alloy" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4a4aaae80afd4be443a6aecd92a6b255dcdd000f97996928efb33d8a71e100" +dependencies = [ + "alloy-consensus 0.2.1", + "alloy-contract 0.2.1", + "alloy-core 0.7.7", + "alloy-eips 0.2.1", + "alloy-genesis 0.2.1", + "alloy-network 0.2.1", + "alloy-provider 0.2.1", + "alloy-pubsub", + "alloy-rpc-client 0.2.1", + "alloy-rpc-types", + "alloy-serde 0.2.1", + "alloy-signer 0.2.1", + "alloy-signer-local", + "alloy-transport 0.2.1", + "alloy-transport-http 0.2.1", + "alloy-transport-ipc", + "alloy-transport-ws", +] + +[[package]] +name = "alloy" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c37d89f69cb43901949ba29307ada8b9e3b170f94057ad4c04d6fd169d24d65f" +dependencies = [ + "alloy-consensus 0.3.3", + "alloy-contract 0.3.3", + "alloy-core 0.8.3", + "alloy-eips 0.3.3", + "alloy-genesis 0.3.3", + "alloy-network 0.3.3", + "alloy-provider 0.3.3", + "alloy-rpc-client 0.3.3", + "alloy-serde 0.3.3", + "alloy-transport 0.3.3", + "alloy-transport-http 0.3.3", +] + +[[package]] +name = "alloy-chains" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b4f201b0ac8f81315fbdc55269965a8ddadbc04ab47fa65a1a468f9a40f7a5f" +dependencies = [ + "num_enum", + "strum", +] + +[[package]] +name = "alloy-consensus" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c309895995eaa4bfcc345f5515a39c7df9447798645cc8bf462b6c5bf1dc96" +dependencies = [ + "alloy-eips 0.2.1", + "alloy-primitives 0.7.7", + "alloy-rlp", + "alloy-serde 0.2.1", + "c-kzg", + "serde", +] + +[[package]] +name = "alloy-consensus" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1468e3128e07c7afe4ff13c17e8170c330d12c322f8924b8bf6986a27e0aad3d" +dependencies = [ + "alloy-eips 0.3.3", + "alloy-primitives 0.8.3", + "alloy-rlp", + "alloy-serde 0.3.3", + "c-kzg", + "serde", +] + +[[package]] +name = "alloy-contract" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4e0ef72b0876ae3068b2ed7dfae9ae1779ce13cfaec2ee1f08f5bd0348dc57" +dependencies = [ + "alloy-dyn-abi 0.7.7", + "alloy-json-abi 0.7.7", + "alloy-network 0.2.1", + "alloy-network-primitives 0.2.1", + "alloy-primitives 0.7.7", + "alloy-provider 0.2.1", + "alloy-pubsub", + "alloy-rpc-types-eth 0.2.1", + "alloy-sol-types 0.7.7", + "alloy-transport 0.2.1", + "futures", + "futures-util", + "thiserror", +] + +[[package]] +name = "alloy-contract" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335d62de1a887f1b780441f8a3037f39c9fb26839cc9acd891c9b80396145cd5" +dependencies = [ + "alloy-dyn-abi 0.8.3", + "alloy-json-abi 0.8.3", + "alloy-network 0.3.3", + "alloy-network-primitives 0.3.3", + "alloy-primitives 0.8.3", + "alloy-provider 0.3.3", + "alloy-rpc-types-eth 0.3.3", + "alloy-sol-types 0.8.3", + "alloy-transport 0.3.3", + "futures", + "futures-util", + "thiserror", +] + +[[package]] +name = "alloy-core" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "529fc6310dc1126c8de51c376cbc59c79c7f662bd742be7dc67055d5421a81b4" +dependencies = [ + "alloy-dyn-abi 0.7.7", + "alloy-json-abi 0.7.7", + "alloy-primitives 0.7.7", + "alloy-sol-types 0.7.7", +] + +[[package]] +name = "alloy-core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b095eb0533144b4497e84a9cc3e44a5c2e3754a3983c0376a55a2f9183a53e" +dependencies = [ + "alloy-dyn-abi 0.8.3", + "alloy-json-abi 0.8.3", + "alloy-primitives 0.8.3", + "alloy-sol-types 0.8.3", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413902aa18a97569e60f679c23f46a18db1656d87ab4d4e49d0e1e52042f66df" +dependencies = [ + "alloy-json-abi 0.7.7", + "alloy-primitives 0.7.7", + "alloy-sol-type-parser 0.7.7", + "alloy-sol-types 0.7.7", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4004925bff5ba0a11739ae84dbb6601a981ea692f3bd45b626935ee90a6b8471" +dependencies = [ + "alloy-json-abi 0.8.3", + "alloy-primitives 0.8.3", + "alloy-sol-type-parser 0.8.3", + "alloy-sol-types 0.8.3", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow", +] + +[[package]] +name = "alloy-eip2930" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" +dependencies = [ + "alloy-primitives 0.8.3", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d319bb544ca6caeab58c39cea8921c55d924d4f68f2c60f24f914673f9a74a" +dependencies = [ + "alloy-primitives 0.8.3", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eips" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9431c99a3b3fe606ede4b3d4043bdfbcb780c45b8d8d226c3804e2b75cfbe68" +dependencies = [ + "alloy-primitives 0.7.7", + "alloy-rlp", + "alloy-serde 0.2.1", + "c-kzg", + "derive_more 0.99.18", + "k256", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-eips" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c35df7b972b06f1b2f4e8b7a53328522fa788054a9d3e556faf2411c5a51d5a" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives 0.8.3", + "alloy-rlp", + "alloy-serde 0.3.3", + "c-kzg", + "derive_more 1.0.0", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-genesis" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79614dfe86144328da11098edcc7bc1a3f25ad8d3134a9eb9e857e06f0d9840d" +dependencies = [ + "alloy-primitives 0.7.7", + "alloy-serde 0.2.1", + "serde", +] + +[[package]] +name = "alloy-genesis" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7210f9206c0fa2a83c824cf8cb6c962126bc9fdc4f41ade1932f14150ef5f6" +dependencies = [ + "alloy-primitives 0.8.3", + "alloy-serde 0.3.3", + "serde", +] + +[[package]] +name = "alloy-json-abi" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc05b04ac331a9f07e3a4036ef7926e49a8bf84a99a1ccfc7e2ab55a5fcbb372" +dependencies = [ + "alloy-primitives 0.7.7", + "alloy-sol-type-parser 0.7.7", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-abi" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9996daf962fd0a90d3c93b388033228865953b92de7bb1959b891d78750a4091" +dependencies = [ + "alloy-primitives 0.8.3", + "alloy-sol-type-parser 0.8.3", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e2865c4c3bb4cdad3f0d9ec1ab5c0c657ba69a375651bd35e32fb6c180ccc2" +dependencies = [ + "alloy-primitives 0.7.7", + "alloy-sol-types 0.7.7", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8866562186d237f1dfeaf989ef941a24764f764bf5c33311e37ead3519c6a429" +dependencies = [ + "alloy-primitives 0.8.3", + "alloy-sol-types 0.8.3", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e701fc87ef9a3139154b0b4ccb935b565d27ffd9de020fe541bf2dec5ae4ede" +dependencies = [ + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-json-rpc 0.2.1", + "alloy-network-primitives 0.2.1", + "alloy-primitives 0.7.7", + "alloy-rpc-types-eth 0.2.1", + "alloy-serde 0.2.1", + "alloy-signer 0.2.1", + "alloy-sol-types 0.7.7", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] + +[[package]] +name = "alloy-network" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe714e233f9eaf410de95a9af6bcd05d3a7f8c8de7a0817221e95a6b642a080" +dependencies = [ + "alloy-consensus 0.3.3", + "alloy-eips 0.3.3", + "alloy-json-rpc 0.3.3", + "alloy-network-primitives 0.3.3", + "alloy-primitives 0.8.3", + "alloy-rpc-types-eth 0.3.3", + "alloy-serde 0.3.3", + "alloy-signer 0.3.3", + "alloy-sol-types 0.8.3", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] + +[[package]] +name = "alloy-network-primitives" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9d5a0f9170b10988b6774498a022845e13eda94318440d17709d50687f67f9" +dependencies = [ + "alloy-primitives 0.7.7", + "alloy-serde 0.2.1", + "serde", +] + +[[package]] +name = "alloy-network-primitives" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c5a38117974c5776a45e140226745a0b664f79736aa900995d8e4121558e064" +dependencies = [ + "alloy-eips 0.3.3", + "alloy-primitives 0.8.3", + "alloy-serde 0.3.3", + "serde", +] + +[[package]] +name = "alloy-primitives" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 0.99.18", + "hex-literal", + "itoa", + "k256", + "keccak-asm", + "proptest", + "rand", + "ruint", + "serde", + "tiny-keccak", +] + +[[package]] +name = "alloy-primitives" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 1.0.0", + "hex-literal", + "itoa", + "k256", + "keccak-asm", + "proptest", + "rand", + "ruint", + "serde", + "tiny-keccak", +] + +[[package]] +name = "alloy-provider" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9c0ab10b93de601a6396fc7ff2ea10d3b28c46f079338fa562107ebf9857c8" +dependencies = [ + "alloy-chains", + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-json-rpc 0.2.1", + "alloy-network 0.2.1", + "alloy-network-primitives 0.2.1", + "alloy-primitives 0.7.7", + "alloy-pubsub", + "alloy-rpc-client 0.2.1", + "alloy-rpc-types-eth 0.2.1", + "alloy-transport 0.2.1", + "alloy-transport-http 0.2.1", + "alloy-transport-ipc", + "alloy-transport-ws", + "async-stream", + "async-trait", + "auto_impl", + "dashmap 5.5.3", + "futures", + "futures-utils-wasm", + "lru", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "alloy-provider" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c65633d6ef83c3626913c004eaf166a6dd50406f724772ea8567135efd6dc5d3" +dependencies = [ + "alloy-chains", + "alloy-consensus 0.3.3", + "alloy-eips 0.3.3", + "alloy-json-rpc 0.3.3", + "alloy-network 0.3.3", + "alloy-network-primitives 0.3.3", + "alloy-primitives 0.8.3", + "alloy-rpc-client 0.3.3", + "alloy-rpc-types-eth 0.3.3", + "alloy-transport 0.3.3", + "alloy-transport-http 0.3.3", + "async-stream", + "async-trait", + "auto_impl", + "dashmap 6.1.0", + "futures", + "futures-utils-wasm", + "lru", + "pin-project", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "alloy-pubsub" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f5da2c55cbaf229bad3c5f8b00b5ab66c74ef093e5f3a753d874cfecf7d2281" +dependencies = [ + "alloy-json-rpc 0.2.1", + "alloy-primitives 0.7.7", + "alloy-transport 0.2.1", + "bimap", + "futures", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tracing", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26154390b1d205a4a7ac7352aa2eb4f81f391399d4e2f546fb81a2f8bb383f62" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "alloy-rpc-client" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b38e3ffdb285df5d9f60cb988d336d9b8e3505acb78750c3bc60336a7af41d3" +dependencies = [ + "alloy-json-rpc 0.2.1", + "alloy-primitives 0.7.7", + "alloy-pubsub", + "alloy-transport 0.2.1", + "alloy-transport-http 0.2.1", + "alloy-transport-ipc", + "alloy-transport-ws", + "futures", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tracing", + "url", +] + +[[package]] +name = "alloy-rpc-client" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fc328bb5d440599ba1b5aa44c0b9ab0625fbc3a403bb5ee94ed4a01ba23e07" +dependencies = [ + "alloy-json-rpc 0.3.3", + "alloy-transport 0.3.3", + "alloy-transport-http 0.3.3", + "futures", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower 0.5.1", + "tracing", + "url", +] + +[[package]] +name = "alloy-rpc-types" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c31a3750b8f5a350d17354e46a52b0f2f19ec5f2006d816935af599dedc521" +dependencies = [ + "alloy-rpc-types-engine", + "alloy-rpc-types-eth 0.2.1", + "alloy-serde 0.2.1", + "serde", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff63f51b2fb2f547df5218527fd0653afb1947bf7fead5b3ce58c75d170b30f7" +dependencies = [ + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-primitives 0.7.7", + "alloy-rlp", + "alloy-rpc-types-eth 0.2.1", + "alloy-serde 0.2.1", + "jsonwebtoken", + "rand", + "serde", + "thiserror", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e18424d962d7700a882fe423714bd5b9dde74c7a7589d4255ea64068773aef" +dependencies = [ + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-network-primitives 0.2.1", + "alloy-primitives 0.7.7", + "alloy-rlp", + "alloy-serde 0.2.1", + "alloy-sol-types 0.7.7", + "itertools 0.13.0", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a59b1d7c86e0a653e7f3d29954f6de5a2878d8cfd1f010ff93be5c2c48cd3b1" +dependencies = [ + "alloy-consensus 0.3.3", + "alloy-eips 0.3.3", + "alloy-network-primitives 0.3.3", + "alloy-primitives 0.8.3", + "alloy-rlp", + "alloy-serde 0.3.3", + "alloy-sol-types 0.8.3", + "itertools 0.13.0", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-serde" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33feda6a53e6079895aed1d08dcb98a1377b000d80d16370fbbdb8155d547ef" +dependencies = [ + "alloy-primitives 0.7.7", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-serde" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51db8a6428a2159e01b7a43ec7aac801edd0c4db1d4de06f310c288940f16fd3" +dependencies = [ + "alloy-primitives 0.8.3", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-signer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740a25b92e849ed7b0fa013951fe2f64be9af1ad5abe805037b44fb7770c5c47" +dependencies = [ + "alloy-primitives 0.7.7", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror", +] + +[[package]] +name = "alloy-signer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebc1760c13592b7ba3fcd964abba546b8d6a9f10d15e8d92a8263731be33f36" +dependencies = [ + "alloy-primitives 0.8.3", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror", +] + +[[package]] +name = "alloy-signer-local" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b0707d4f63e4356a110b30ef3add8732ab6d181dd7be4607bf79b8777105cee" +dependencies = [ + "alloy-consensus 0.2.1", + "alloy-network 0.2.1", + "alloy-primitives 0.7.7", + "alloy-signer 0.2.1", + "async-trait", + "k256", + "rand", + "thiserror", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" +dependencies = [ + "alloy-sol-macro-expander 0.7.7", + "alloy-sol-macro-input 0.7.7", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0458ccb02a564228fcd76efb8eb5a520521a8347becde37b402afec9a1b83859" +dependencies = [ + "alloy-sol-macro-expander 0.8.3", + "alloy-sol-macro-input 0.8.3", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" +dependencies = [ + "alloy-json-abi 0.7.7", + "alloy-sol-macro-input 0.7.7", + "const-hex", + "heck 0.5.0", + "indexmap", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.77", + "syn-solidity 0.7.7", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc65475025fc1e84bf86fc840f04f63fcccdcf3cf12053c99918e4054dfbc69" +dependencies = [ + "alloy-json-abi 0.8.3", + "alloy-sol-macro-input 0.8.3", + "const-hex", + "heck 0.5.0", + "indexmap", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.77", + "syn-solidity 0.8.3", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" +dependencies = [ + "alloy-json-abi 0.7.7", + "const-hex", + "dunce", + "heck 0.5.0", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.77", + "syn-solidity 0.7.7", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed10f0715a0b69fde3236ff3b9ae5f6f7c97db5a387747100070d3016b9266b" +dependencies = [ + "alloy-json-abi 0.8.3", + "const-hex", + "dunce", + "heck 0.5.0", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.77", + "syn-solidity 0.8.3", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edae8ea1de519ccba896b6834dec874230f72fe695ff3c9c118e90ec7cff783" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-types" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" +dependencies = [ + "alloy-json-abi 0.7.7", + "alloy-primitives 0.7.7", + "alloy-sol-macro 0.7.7", + "const-hex", + "serde", +] + +[[package]] +name = "alloy-sol-types" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1eb88e4da0a1b697ed6a9f811fdba223cf4d5c21410804fd1707836af73a462b" +dependencies = [ + "alloy-json-abi 0.8.3", + "alloy-primitives 0.8.3", + "alloy-sol-macro 0.8.3", + "const-hex", + "serde", +] + +[[package]] +name = "alloy-transport" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d0590afbdacf2f8cca49d025a2466f3b6584a016a8b28f532f29f8da1007bae" +dependencies = [ + "alloy-json-rpc 0.2.1", + "base64 0.22.1", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower 0.4.13", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5dc4e902f1860d54952446d246ac05386311ad61030a2b906ae865416d36e0" +dependencies = [ + "alloy-json-rpc 0.3.3", + "base64 0.22.1", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower 0.5.1", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-http" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2437d145d80ea1aecde8574d2058cceb8b3c9cba05f6aea8e67907c660d46698" +dependencies = [ + "alloy-json-rpc 0.2.1", + "alloy-transport 0.2.1", + "reqwest", + "serde_json", + "tower 0.4.13", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-http" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1742b94bb814f1ca6b322a6f9dd38a0252ff45a3119e40e888fb7029afa500ce" +dependencies = [ + "alloy-json-rpc 0.3.3", + "alloy-transport 0.3.3", + "reqwest", + "serde_json", + "tower 0.5.1", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-ipc" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804494366e20468776db4e18f9eb5db7db0fe14f1271eb6dbf155d867233405c" +dependencies = [ + "alloy-json-rpc 0.2.1", + "alloy-pubsub", + "alloy-transport 0.2.1", + "bytes", + "futures", + "interprocess", + "pin-project", + "serde_json", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "alloy-transport-ws" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af855163e7df008799941aa6dd324a43ef2bf264b08ba4b22d44aad6ced65300" +dependencies = [ + "alloy-pubsub", + "alloy-transport 0.2.1", + "futures", + "http", + "rustls", + "serde_json", + "tokio", + "tokio-tungstenite", + "tracing", + "ws_stream_wasm", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" + +[[package]] +name = "apps" +version = "0.1.0" +dependencies = [ + "alloy 0.2.1", + "alloy-primitives 0.8.3", + "alloy-sol-types 0.8.3", + "anyhow", + "clap", + "env_logger", + "governance-methods", + "hex", + "log", + "risc0-ethereum-contracts", + "risc0-zkvm", + "tokio", + "tracing-subscriber 0.3.18", + "url", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-crypto-primitives" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3a13b34da09176a8baba701233fdffbaa7c1b1192ce031a3da4e55ce1f1a56" +dependencies = [ + "ark-ec", + "ark-ff 0.4.2", + "ark-relations", + "ark-serialize 0.4.2", + "ark-snark", + "ark-std 0.4.0", + "blake2", + "derivative", + "digest 0.10.7", + "sha2", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff 0.4.2", + "ark-poly", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-groth16" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20ceafa83848c3e390f1cbf124bc3193b3e639b3f02009e0e290809a501b95fc" +dependencies = [ + "ark-crypto-primitives", + "ark-ec", + "ark-ff 0.4.2", + "ark-poly", + "ark-relations", + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-relations" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00796b6efc05a3f48225e59cb6a2cda78881e7c390872d5786aaf112f31fb4f0" +dependencies = [ + "ark-ff 0.4.2", + "ark-std 0.4.0", + "tracing", + "tracing-subscriber 0.2.25", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-snark" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d3cc6833a335bb8a600241889ead68ee89a3cf8448081fb7694c0fe503da63" +dependencies = [ + "ark-ff 0.4.2", + "ark-relations", + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "async-trait" +version = "0.1.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version 0.4.1", +] + +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blst" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "bonsai-sdk" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68872e247f6fcf694ecbb884832a705cb2ae09f239cbbcc8bf71ed593d609a45" +dependencies = [ + "duplicate", + "maybe-async", + "reqwest", + "serde", + "thiserror", +] + +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.77", + "syn_derive", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "bytemuck" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +dependencies = [ + "serde", +] + +[[package]] +name = "c-kzg" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0307f72feab3300336fb803a57134159f6e20139af1357f36c54cb90d8e8928" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", + "serde", +] + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.23", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "clap" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "const-hex" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.1", + "syn 2.0.77", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "docker-generate" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf673e0848ef09fa4aeeba78e681cf651c0c7d35f76ee38cec8e55bc32fa111" + +[[package]] +name = "doctest-file" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "duplicate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de78e66ac9061e030587b2a2e75cc88f22304913c907b11307bca737141230cb" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "governance-methods" +version = "0.1.0" +dependencies = [ + "alloy-primitives 0.8.3", + "alloy-sol-types 0.8.3", + "hex", + "risc0-build", + "risc0-build-ethereum", + "risc0-zkp", + "risc0-zkvm", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower 0.4.13", + "tower-service", + "tracing", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + +[[package]] +name = "interprocess" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f4e4a06d42fab3e85ab1b419ad32b09eab58b901d40c57935ff92db3287a13" +dependencies = [ + "doctest-file", + "futures-core", + "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.52.0", +] + +[[package]] +name = "ipnet" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "9.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +dependencies = [ + "base64 0.21.7", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", +] + +[[package]] +name = "keccak-asm" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422fbc7ff2f2f5bdffeb07718e5a5324dca72b0c9293d50df4026652385e3314" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "lazy-regex" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d8e41c97e6bc7ecb552016274b99fbb5d035e8de288c582d9b933af6677bfda" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e1d8b05d672c53cb9c7b920bbba8783845ae4f0b076e02a3db1d02c81b4163" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.77", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "maybe-async" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "metal" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +dependencies = [ + "bitflags 2.6.0", + "block", + "core-graphics-types", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parity-scale-codec" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version 0.4.1", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.6.0", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax 0.8.4", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "prost" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" +dependencies = [ + "anyhow", + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quinn" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash", + "rustls", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "reqwest" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", + "windows-registry", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "risc0-binfmt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbac77ca59e4e1d765141d93ca72f13b632e374c69ae1b18a770b425aeecafce" +dependencies = [ + "anyhow", + "borsh", + "elf", + "risc0-zkp", + "risc0-zkvm-platform", + "serde", + "tracing", +] + +[[package]] +name = "risc0-build" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f50757e90ff58c227a46774ecda4c927aefa3567f0851d341def452c098737b" +dependencies = [ + "anyhow", + "cargo_metadata", + "dirs", + "docker-generate", + "hex", + "risc0-binfmt", + "risc0-zkp", + "risc0-zkvm-platform", + "serde", + "serde_json", + "tempfile", +] + +[[package]] +name = "risc0-build-ethereum" +version = "1.1.0" +dependencies = [ + "anyhow", + "hex", + "risc0-build", + "risc0-zkp", +] + +[[package]] +name = "risc0-circuit-recursion" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabb4f5abdab268f8974d9e90ce09752be57f2cadc8ad6f7fae9a15a6ddc6773" +dependencies = [ + "anyhow", + "bytemuck", + "hex", + "metal", + "risc0-core", + "risc0-zkp", + "tracing", +] + +[[package]] +name = "risc0-circuit-rv32im" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8473dbe644e94a679d13b7e53a28c7c086b260a43d426259b5d74c862f2727de" +dependencies = [ + "anyhow", + "metal", + "risc0-binfmt", + "risc0-core", + "risc0-zkp", + "risc0-zkvm-platform", + "serde", + "tracing", +] + +[[package]] +name = "risc0-core" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de09abf5e0b102e69d96213e643fd7ae320ed0ca3fad3cd4eed8ce5fbab06bc" +dependencies = [ + "bytemuck", + "rand_core", +] + +[[package]] +name = "risc0-ethereum-contracts" +version = "1.1.0" +dependencies = [ + "alloy 0.3.3", + "anyhow", + "risc0-zkvm", +] + +[[package]] +name = "risc0-groth16" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17e33bc37e7797d663b1e780f09cb295ffeb80712621bf3003ce79f56b66033d" +dependencies = [ + "anyhow", + "ark-bn254", + "ark-ec", + "ark-groth16", + "ark-serialize 0.4.2", + "bytemuck", + "hex", + "num-bigint", + "num-traits", + "risc0-binfmt", + "risc0-zkp", + "serde", +] + +[[package]] +name = "risc0-zkp" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83ae2c52905c83a62275ec75ddb60b8cdcf2388ae4add58a727f68822b4be93c" +dependencies = [ + "anyhow", + "blake2", + "borsh", + "bytemuck", + "cfg-if", + "digest 0.10.7", + "hex", + "hex-literal", + "metal", + "paste", + "rand_core", + "risc0-core", + "risc0-zkvm-platform", + "serde", + "sha2", + "tracing", +] + +[[package]] +name = "risc0-zkvm" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f99159c6f87ab49222d44a68b2de5bc3182b177d6307fb1eed6f1c43e5baa163" +dependencies = [ + "anyhow", + "bincode", + "bonsai-sdk", + "borsh", + "bytemuck", + "bytes", + "getrandom", + "hex", + "lazy-regex", + "prost", + "risc0-binfmt", + "risc0-build", + "risc0-circuit-recursion", + "risc0-circuit-rv32im", + "risc0-core", + "risc0-groth16", + "risc0-zkp", + "risc0-zkvm-platform", + "rrs-lib", + "semver 1.0.23", + "serde", + "sha2", + "stability", + "tempfile", + "tracing", +] + +[[package]] +name = "risc0-zkvm-platform" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12c53def950c8c8d25f9256af2a3e02a6284774c8ee31eed5d56c3533fbcec2e" +dependencies = [ + "bytemuck", + "getrandom", + "libm", + "stability", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rrs-lib" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4382d3af3a4ebdae7f64ba6edd9114fff92c89808004c4943b393377a25d001" +dependencies = [ + "downcast-rs", + "paste", +] + +[[package]] +name = "ruint" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.23", +] + +[[package]] +name = "rustix" +version = "0.38.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3-asm" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d79b758b7cb2085612b11a235055e485605a5103faccdd633f35bd7aee69dd" +dependencies = [ + "cc", + "cfg-if", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core", +] + +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stability" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" +dependencies = [ + "quote", + "syn 2.0.77", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.77", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "syn-solidity" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b95156f8b577cb59dc0b1df15c6f29a10afc5f8a7ac9786b0b5c68c19149278" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ws_stream_wasm" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version 0.4.1", + "send_wrapper", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] diff --git a/examples/governance/Cargo.toml b/examples/governance/Cargo.toml new file mode 100644 index 00000000..ef3b9706 --- /dev/null +++ b/examples/governance/Cargo.toml @@ -0,0 +1,35 @@ +[workspace] +resolver = "2" +members = ["apps", "methods"] +exclude = ["lib"] + +[workspace.package] +version = "0.1.0" +edition = "2021" + +[workspace.dependencies] +# Intra-workspace dependencies +risc0-build-ethereum = { path = "../../build" } +risc0-ethereum-contracts = { path = "../../contracts" } + +# risc0 monorepo dependencies. +risc0-build = { version = "1.1.0", features = ["docker"] } +risc0-zkvm = { version = "1.1.0", default-features = false } +risc0-zkp = { version = "1.1.0", default-features = false } + +alloy-primitives = { version = "0.8", features = ["rlp", "serde", "std"] } +alloy-sol-types = { version = "0.8" } +anyhow = { version = "1.0.75" } +bincode = { version = "1.3" } +bytemuck = { version = "1.14" } +hex = { version = "0.4" } +log = { version = "0.4" } +governance-methods = { path = "./methods" } +serde = { version = "1.0", features = ["derive", "std"] } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tokio = { version = "1.39", features = ["full"] } +url = { version = "2.5" } + +[profile.release] +debug = 1 +lto = true diff --git a/examples/governance/README.md b/examples/governance/README.md new file mode 100644 index 00000000..ac8d7709 --- /dev/null +++ b/examples/governance/README.md @@ -0,0 +1,78 @@ +# RISC Zero Governor + +To run this example on your machine, follow these [instructions]. + +## Abstract + +This example contains a modified version of OpenZeppelin's [Governor] example, which is the standard for DAO Governance. The modifications have one *end goal*: to **save gas** by taking gas intensive computations *offchain*, using RISC Zero's [zkVM], while maintaining the same trust assumptions. + +## How can we take computation offchain and still trust it? + +RISC Zero's [zkVM] leverages ZK technology to provide verifiable computation; it does this by proving the correct execution of Rust code. With this proof, anyone can verify that the computation ran correctly and produced the associated outputs. + +For blockchain applications, this proof/verify workflow directly enables strong compute scaling. Expensive computation can be taken offchain, while the proof verification can be done onchain. As long as the proof is valid, the associated contract state can be updated; you do *not* have to trust the party that generates the proof (the prover). + +You can read more at [What does RISC Zero enable]. + +## How much gas is saved? + +The more accounts cast a vote, the more signature verifications ([ecrecover]) are moved from the EVM to RISC Zero's zkVM. + +![Gas Data Comparison](contracts/test/benchmarks/gas_data_comparison.png) + +

+ Figure 1: A direct comparison of a test voting workflow in BaselineGovernor (the OpenZeppelin implementation) and RiscZeroGovernor (a modified Governor that utilises offchain compute for signature verification. The relevant test files are located in tests/benchmarks. +

+ +The x-axis details the number of votes (also the number of accounts, in the testing workflow, each account votes once), and the y-axis the amount of gas spent. This data was generated using [Foundry], specifically its gas reporting and fuzz testing features. Each workflow with a specific number of votes is run 1000 times to provide an average value. + +## What computation is taken offchain? + +The guest program is located at [finalize_votes.rs]. This is the program that runs in the zkVM and a proof of its correct execution is generated. + +At a high level, `finalize_votes.rs`: +- handles signature verification of each vote +- maintains a verifiable hash of all vote data (see `ballotHash` below) +- handles vote updates (only latest vote per address counts) + +## How is this verified onchain? + +[RiscZeroGovernor.sol] has an important function `verifyAndFinalizeVotes`: + +```solidity + function verifyAndFinalizeVotes( + bytes calldata seal, + bytes calldata journal + ) public { + // verify the proof + verifier.verify(seal, imageId, sha256(journal)); + + // decode the journal + uint256 proposalId = uint256(bytes32(journal[0:32])); + bytes32 ballotHash = bytes32(journal[32:64]); + bytes calldata votingData = journal[64:]; + + _finalizeVotes(proposalId, ballotHash, votingData); + } +``` + +This function calls a RISC Zero [verifier contract] to verify the RISC Zero `Groth16Receipt` produced by the prover. If this proof is invalid, the execution will revert within this call, otherwise the [journal] is decoded and the `_finalizeVotes` function within `RiscZeroGovernorCounting.sol` is called. `_finalizeVotes` handles the votes commited to the journal in 100 byte chunks in `votingData`. + +When a user votes, this has both an onchain and an offchain aspect. The vote is processed offchain as seen in [finalize_votes.rs], and onchain `castVote` is called ([RiscZeroGovernor.sol]), which commits the vote by hashing its vote support (a `uint8` representing the vote state, i.e. 1 represents a `for` vote) and the account's address with a hash accumulator of all previous votes. + +This hash accumulator (`ballotHash`) is a commitment that allows offchain voting state to be matched with state onchain; the order of voting will change the final hash and so `ballotHash` is a running representation of voting in an exact order. If an account votes more than once, there is logic to handle only its latest vote as the valid vote, but its data for previous votes is still hashed into `ballotHash`. + +[ecrecover]: https://docs.soliditylang.org/en/latest/cheatsheet.html#index-7 +[finalize_votes.rs]: ./methods/guest/src/bin/finalize_votes.rs +[Foundry]: https://book.getfoundry.sh/ +[Governor]: https://docs.openzeppelin.com/contracts/4.x/governance +[instructions]: ./instructions.md +[journal]: https://dev.risczero.com/terminology#journal +[RiscZeroGovernor.sol]: ./contracts/RiscZeroGovernor.sol +[verifier contract]: https://dev.risczero.com/api/blockchain-integration/contracts/verifier +[What does Risc Zero enable]: https://dev.risczero.com/api/use-cases +[zkVM]: https://dev.risczero.com/zkvm + + + + diff --git a/examples/governance/apps/Cargo.toml b/examples/governance/apps/Cargo.toml new file mode 100644 index 00000000..d7bd22b4 --- /dev/null +++ b/examples/governance/apps/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "apps" +version = { workspace = true } +edition = { workspace = true } + +[dependencies] +alloy = { version = "0.2", features = ["full"] } +alloy-primitives = { workspace = true } +alloy-sol-types = { workspace = true } +anyhow = { workspace = true } +clap = { version = "4.5", features = ["derive", "env"] } +env_logger = { version = "0.10" } +governance-methods = { workspace = true } +hex = { workspace = true } +log = { workspace = true } +risc0-ethereum-contracts = { workspace = true } +risc0-zkvm = { workspace = true, features = ["client"] } +tokio = { workspace = true, features = ["full"] } +tracing-subscriber = { workspace = true } +url = { workspace = true } + diff --git a/examples/governance/apps/README.md b/examples/governance/apps/README.md new file mode 100644 index 00000000..835ecabd --- /dev/null +++ b/examples/governance/apps/README.md @@ -0,0 +1,24 @@ +# Apps + +In typical applications, an off-chain app is needed to do two main actions: + +* Produce a proof (see [proving options][proving-options]). +* Send a transaction to Ethereum to execute your on-chain logic. + +This template provides the `publisher` CLI as an example application to execute these steps. +In a production application, a back-end server or your dApp client may take on this role. + +## Publisher + +The [`publisher` CLI][publisher], is an example application that produces a proof and publishes it to your app contract. + +### Usage + +Run the `publisher` with: + +```sh +cargo run --bin publisher +``` + +[proving-options]: https://dev.risczero.com/api/generating-proofs/proving-options +[publisher]: ./src/bin/publisher.rs diff --git a/examples/governance/apps/src/bin/publisher.rs b/examples/governance/apps/src/bin/publisher.rs new file mode 100644 index 00000000..efcdce92 --- /dev/null +++ b/examples/governance/apps/src/bin/publisher.rs @@ -0,0 +1,140 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This application demonstrates how to send an off-chain proof request +// to the Bonsai proving service and publish the received proofs directly +// to your deployed app contract. + +use anyhow::{Context, Result}; +use clap::Parser; + +use alloy::{ + network::{EthereumWallet, TransactionBuilder}, + primitives::{Address, Bytes}, + providers::{Provider, ProviderBuilder}, + rpc::types::TransactionRequest, + signers::local::PrivateKeySigner, + sol, + // sol_types::SolInterface +}; + +use governance_methods::FINALIZE_VOTES_ELF; +use risc0_ethereum_contracts::encode_seal; +use risc0_zkvm::{default_prover, ExecutorEnv, ProverOpts, VerifierContext}; +// use tokio::task; +use tracing_subscriber::EnvFilter; +use url::Url; + +sol! { + /// ERC-20 balance function signature. + /// This must match the signature in the guest. + interface RiscZeroGovernor { + function verifyAndFinalizeVotes(bytes calldata seal, bytes calldata journal) public; + } +} + +/// Arguments of the publisher CLI. +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +struct Args { + /// Ethereum Wallet Private Key + #[clap(long, env)] + eth_wallet_private_key: PrivateKeySigner, + + /// Node RPC URL + #[clap(long)] + rpc_url: Url, + + /// Application's contract address on Ethereum + #[clap(long)] + contract: Address, + + /// The proposal ID (32 bytes, hex-encoded) + #[clap(long)] + proposal_id: Bytes, + + /// The votes data (hex-encoded, multiple of 100 bytes) + #[clap(long)] + votes_data: Bytes, +} + +#[tokio::main] +async fn main() -> Result<()> { + // Initialize tracing. In order to view logs, run `RUST_LOG=info cargo run` + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .init(); + // Parse the command line arguments. + let args = Args::parse(); + + // Create an alloy provider for that private key and URL. + let wallet = EthereumWallet::from(args.eth_wallet_private_key); + let provider = ProviderBuilder::new() + .with_recommended_fillers() + .wallet(wallet) + .on_http(args.rpc_url); + + // Decode the hex-encoded proposal ID and votes data + let proposal_id = hex::decode(&args.proposal_id).context("Failed to decode proposal ID")?; + let votes_data = hex::decode(&args.votes_data).context("Failed to decode votes data")?; + + // Validate input lengths + if proposal_id.len() != 32 { + return Err(anyhow::anyhow!("Proposal ID must be 32 bytes")); + } + if votes_data.len() % 100 != 0 { + return Err(anyhow::anyhow!( + "Votes data must be a multiple of 100 bytes" + )); + } + + // Combine proposal ID and votes data + let input = [&proposal_id[..], &votes_data[..]].concat(); + + let env = ExecutorEnv::builder().write_slice(&input).build()?; + + let receipt = default_prover() + .prove_with_ctx( + env, + &VerifierContext::default(), + FINALIZE_VOTES_ELF, + &ProverOpts::groth16(), + )? + .receipt; + + // Encode the seal with the selector. + let seal = encode_seal(&receipt)?; + + // Extract the journal from the receipt. + let journal = receipt.journal.bytes.clone(); + + // build calldata + let calldata = RiscZeroGovernor::verifyAndFinalizeVotesCall { + seal: seal.into(), + journal: journal.into(), + }; + + // send tx to callback function: verifyAndFinalizeVotes + let contract = args.contract; + let tx = TransactionRequest::default() + .with_to(contract) + .with_call(&calldata); + let tx_hash = provider + .send_transaction(tx) + .await + .context("Failed to send transaction")?; + println!("Transaction sent with hash: {:?}", tx_hash); + + Ok(()) +} diff --git a/examples/governance/contracts/script/DeployRiscZeroGovernor.s.sol b/examples/governance/contracts/script/DeployRiscZeroGovernor.s.sol new file mode 100644 index 00000000..39320884 --- /dev/null +++ b/examples/governance/contracts/script/DeployRiscZeroGovernor.s.sol @@ -0,0 +1,97 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.8.17; + +import {Script} from "forge-std/Script.sol"; +import {console2} from "forge-std/console2.sol"; +import {RiscZeroCheats} from "risc0/test/RiscZeroCheats.sol"; +import {ImageID} from "../src/ImageID.sol"; +import {ControlID, RiscZeroGroth16Verifier} from "risc0/groth16/RiscZeroGroth16Verifier.sol"; +import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol"; +import {IVotes} from "openzeppelin/contracts/governance/utils/IVotes.sol"; + +import {RiscZeroGovernor} from "../src/RiscZeroGovernor.sol"; +import {VoteToken} from "../src/VoteToken.sol"; + +/// @notice deployment script for RiscZeroGovernor and it's dependencies. +/// @dev Use the following environment variables to control the deployment: +/// * DEPLOYER_ADDRESS address of the wallet to be used for sending deploy transactions. +/// Must be unlocked on the RPC provider node. +/// * DEPLOYER_PRIVATE_KEY private key of the wallet to be used for deployment. +/// Alternative to using DEPLOYER_ADDRESS. +/// * DEPLOY_VERFIER_ADDRESS address of a predeployed IRiscZeroVerifier contract. +/// If not specified and also DEPLOY_BONSAI_RELAY_ADDRESS is not specified, +/// a new RiscZeroGroth16Verifier will be deployed. +/// * DEPLOY_VOTE_TOKEN_ADDRESS address of a predeployed IVotes contract. +/// If not specified, a new VoteToken contract will be deployed. +/// Note that the deployer address will be the owner of the VoteToken contract. +contract DeployRiscZeroGovernor is Script, RiscZeroCheats { + /// @notice use vm.startBroadcast to begin recording deploy transactions. + function startBroadcast() internal { + address deployerAddr = vm.envOr("DEPLOYER_ADDRESS", address(0)); + uint256 deployerKey = vm.envOr("DEPLOYER_PRIVATE_KEY", uint256(0)); + + if (deployerAddr != address(0) && deployerKey != uint256(0)) { + revert("only one of DEPLOYER_ADDRESS or DEPLOYER_PRIVATE_KEY should be set"); + } + if (deployerAddr != address(0)) { + vm.startBroadcast(deployerAddr); + } else if (deployerKey != uint256(0)) { + vm.startBroadcast(deployerKey); + } else if (block.chainid == 31337) { + // On an Anvil local testnet, use the first private key by default. + deployerKey = uint256(0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80); + vm.startBroadcast(deployerKey); + } else { + revert("specify a deployer with either DEPLOYER_ADDRESS or DEPLOYER_PRIVATE_KEY"); + } + } + + function run() external { + startBroadcast(); + + // Deploy an IRiscZeroVerifier contract. + IRiscZeroVerifier verifier; + address verifierAddr = vm.envOr("DEPLOY_VERFIER_ADDRESS", address(0)); + if (verifierAddr != address(0)) { + console2.log("Using IRiscZeroVerifier at ", address(verifierAddr)); + verifier = IRiscZeroVerifier(verifierAddr); + } else { + verifier = new RiscZeroGroth16Verifier(ControlID.CONTROL_ROOT, ControlID.BN254_CONTROL_ID); + console2.log("Deployed RiscZeroGroth16Verifier to ", address(verifier)); + } + // Deploy the IVotes token used to grant voting rights. + IVotes token; + address tokenAddr = vm.envOr("DEPLOY_VOTE_TOKEN_ADDRESS", address(0)); + if (tokenAddr != address(0)) { + console2.log("Using IVotes at ", address(tokenAddr)); + token = IVotes(tokenAddr); + } else { + // Sender of the transactions will be the owner and controller of the VoteToken. + token = new VoteToken(); + console2.log("Deployed VoteToken to ", address(token)); + } + + // Deploy the RiscZeroGovernor. + // importing ImageID from auto-generated ImageID.sol + bytes32 imageId = ImageID.FINALIZE_VOTES_ID; + RiscZeroGovernor gov = new RiscZeroGovernor(token, imageId, verifier); + console2.log("Deployed RiscZeroGovernor to ", address(gov)); + + vm.stopBroadcast(); + } +} diff --git a/examples/governance/contracts/src/BaselineGovernor.sol b/examples/governance/contracts/src/BaselineGovernor.sol new file mode 100644 index 00000000..af0aaad1 --- /dev/null +++ b/examples/governance/contracts/src/BaselineGovernor.sol @@ -0,0 +1,72 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.8.9; + +import {Governor, IGovernor} from "openzeppelin/contracts/governance/Governor.sol"; +import {GovernorSettings} from "openzeppelin/contracts/governance/extensions/GovernorSettings.sol"; +import {GovernorCountingSimple} from "openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol"; +import {GovernorVotes, IVotes} from "openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; +import {GovernorVotesQuorumFraction} from "openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; +import {IERC6372} from "openzeppelin/contracts/interfaces/IERC6372.sol"; + +import {ExtendedGovernorBase} from "./ExtendedGovernorBase.sol"; + +/// @custom:security-contact security@risczero.com +contract BaselineGovernor is + ExtendedGovernorBase, + GovernorSettings, + GovernorCountingSimple, + GovernorVotes, + GovernorVotesQuorumFraction +{ + constructor(IVotes _token) + Governor("BaselineGovernor") + GovernorSettings(300, /* blocks */ 21000, /* blocks */ 0) + GovernorVotes(_token) + GovernorVotesQuorumFraction(20) + {} + + // The following functions are overrides required by Solidity. + function votingDelay() public view override(Governor, IGovernor, GovernorSettings) returns (uint256) { + return super.votingDelay(); + } + + function votingPeriod() public view override(Governor, IGovernor, GovernorSettings) returns (uint256) { + return super.votingPeriod(); + } + + function quorum(uint256 blockNumber) + public + view + override(Governor, IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function proposalThreshold() public view override(Governor, IGovernor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function clock() public view override(Governor, GovernorVotes, IERC6372) returns (uint48) { + return super.clock(); + } + + function CLOCK_MODE() public view override(Governor, GovernorVotes, IERC6372) returns (string memory) { + return super.CLOCK_MODE(); + } +} diff --git a/examples/governance/contracts/src/ExtendedGovernorBase.sol b/examples/governance/contracts/src/ExtendedGovernorBase.sol new file mode 100644 index 00000000..a8f0b145 --- /dev/null +++ b/examples/governance/contracts/src/ExtendedGovernorBase.sol @@ -0,0 +1,42 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.8.9; + +import {Governor, IGovernor} from "openzeppelin/contracts/governance/Governor.sol"; + +abstract contract ExtendedGovernorBase is IGovernor, Governor { + // Copied from the Governor contract. + /// @notice Calculate the message digest to sign in order to call castVoteBySig. + function voteHash(uint256 proposalId, uint8 support, address voter) public virtual returns (bytes32) { + uint256 nonce = nonces(voter); + return _hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support, voter, nonce))); + } + + /// @notice Calculate the message digest to sign in order to call castVoteBySig. + function voteHashWithReasonAndParamsBySig( + uint256 proposalId, + uint8 support, + string calldata reason, + bytes memory params + ) public view virtual returns (bytes32) { + return _hashTypedDataV4( + keccak256( + abi.encode(EXTENDED_BALLOT_TYPEHASH, proposalId, support, keccak256(bytes(reason)), keccak256(params)) + ) + ); + } +} diff --git a/examples/governance/contracts/src/RiscZeroGovernor.sol b/examples/governance/contracts/src/RiscZeroGovernor.sol new file mode 100644 index 00000000..b49da990 --- /dev/null +++ b/examples/governance/contracts/src/RiscZeroGovernor.sol @@ -0,0 +1,204 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.8.9; + +import {Governor, IGovernor} from "openzeppelin/contracts/governance/Governor.sol"; +import {GovernorSettings} from "openzeppelin/contracts/governance/extensions/GovernorSettings.sol"; +import {GovernorVotes, IVotes} from "openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; +import {GovernorVotesQuorumFraction} from "openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; + +import {RiscZeroGovernorCounting} from "./RiscZeroGovernorCounting.sol"; +import {ExtendedGovernorBase} from "./ExtendedGovernorBase.sol"; +import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol"; + +import {IERC6372} from "openzeppelin/contracts/interfaces/IERC6372.sol"; + +/// @custom:security-contact security@risczero.com +contract RiscZeroGovernor is + ExtendedGovernorBase, + GovernorSettings, + RiscZeroGovernorCounting, + GovernorVotes, + GovernorVotesQuorumFraction +{ + /// @notice RISC Zero zkVM image ID for the vote finalization program. + bytes32 public immutable imageId; + IRiscZeroVerifier public immutable verifier; + + constructor(IVotes token_, bytes32 imageId_, IRiscZeroVerifier verifier_) + Governor("RiscZeroGovernor") + GovernorSettings(300, /* blocks */ 21000, /* blocks */ 0) + GovernorVotes(token_) + GovernorVotesQuorumFraction(20) + { + imageId = imageId_; + verifier = verifier_; + } + + /** + * @notice Calculate the current state of the proposal. + * @dev See {IGovernor-state}. + */ + function state(uint256 proposalId) public view override(IGovernor, Governor) returns (ProposalState) { + ProposalState superState = super.state(proposalId); + + // If the votes have not been finalized, by proving the off-chain verified list of validated + // ballots, then keep the proposal status as active. IGovernor does not provide a state to + // indicate that voting has ended, but the result is unknown. + if (superState == ProposalState.Defeated && !_proposalVotesFinalized(proposalId)) { + return ProposalState.Active; + } + return superState; + } + + /** + * @dev See {IGovernor-castVote}. + * Does not return the voter's balance, since balance lookups are deferred. + */ + function castVote(uint256 proposalId, uint8 support) public override(Governor, IGovernor) returns (uint256) { + address voter = _msgSender(); + _commitVote(proposalId, support, voter); + emit VoteCast(voter, proposalId, support, 0, ""); + return 0; + } + + /** + * @dev See {IGovernor-castVoteWithReason}. + * Does not return the voter's balance, since balance lookups are deferred. + */ + function castVoteWithReason(uint256 proposalId, uint8 support, string calldata reason) + public + override(Governor, IGovernor) + returns (uint256) + { + address voter = _msgSender(); + _commitVote(proposalId, support, voter); + emit VoteCast(voter, proposalId, support, 0, reason); + return 0; + } + + /* + * @dev See {IGovernor-castVoteWithReasonAndParams}. + * Does not return the voter's balance, since balance lookups are deferred. + */ + function castVoteWithReasonAndParams(uint256 proposalId, uint8 support, string calldata reason, bytes memory params) + public + override(Governor, IGovernor) + returns (uint256) + { + require(params.length == 0, "RiscZeroGovernor: params are not supported"); + + address voter = _msgSender(); + _commitVote(proposalId, support, voter); + emit VoteCast(voter, proposalId, support, 0, reason); + return 0; + } + + /** + * @dev See {IGovernor-castVoteBySig}. + * Does not return the voter's balance, since balance lookups are deferred. + * Also does not log a VoteCast event because it cannot be determined yet if this is a valid vote. + */ + function castVoteBySig(uint256 proposalId, uint8 support, address voter, bytes memory signature) + public + override(Governor, IGovernor) + returns (uint256) + { + bytes32 digest = voteHash(proposalId, support, voter); + + _commitVoteBySig(proposalId, support, signature, digest); + return 0; + } + + /** + * @dev See {IGovernor-castVoteWithReasonAndParamsBySig}. + * Does not return the voter's balance, since balance lookups are deferred. + * Also does not log a VoteCast event because it cannot be determined yet if this is a valid vote. + */ + function castVoteWithReasonAndParamsBySig( + uint256 proposalId, + uint8 support, + string calldata reason, + bytes memory params, + bytes memory signature + ) public returns (uint256) { + require(params.length == 0, "RiscZeroGovernor: params are not supported"); + + bytes32 digest = voteHashWithReasonAndParamsBySig(proposalId, support, reason, params); + _commitVoteBySig(proposalId, support, signature, digest); + return 0; + } + + /// @notice verify the proof of the `finalize_votes` guest program and finalize the vote count. + /// `seal`: the seal is a zk-STARK and generated by the prover. + /// `journal`: the public outputs of the computation. + function verifyAndFinalizeVotes(bytes calldata seal, bytes calldata journal) public { + // verify the proof + verifier.verify(seal, imageId, sha256(journal)); + + // decode the journal + uint256 proposalId = uint256(bytes32(journal[0:32])); + bytes32 ballotHash = bytes32(journal[32:64]); + bytes calldata votingData = journal[64:]; + + _finalizeVotes(proposalId, ballotHash, votingData); + } + + function _castVote(uint256, address, uint8, string memory, bytes memory) internal pure override returns (uint256) { + revert("_castVote is not supported"); + } + + // The following functions are overrides required by Solidity. + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(IGovernor, Governor, RiscZeroGovernorCounting) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function votingDelay() public view override(Governor, IGovernor, GovernorSettings) returns (uint256) { + return super.votingDelay(); + } + + function votingPeriod() public view override(Governor, IGovernor, GovernorSettings) returns (uint256) { + return super.votingPeriod(); + } + + function quorum(uint256 blockNumber) + public + view + override(Governor, IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function proposalThreshold() public view override(Governor, IGovernor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function clock() public view override(Governor, GovernorVotes, IERC6372) returns (uint48) { + return super.clock(); + } + + function CLOCK_MODE() public view override(Governor, GovernorVotes, IERC6372) returns (string memory) { + return super.CLOCK_MODE(); + } +} diff --git a/examples/governance/contracts/src/RiscZeroGovernorCounting.sol b/examples/governance/contracts/src/RiscZeroGovernorCounting.sol new file mode 100644 index 00000000..e6c2eddc --- /dev/null +++ b/examples/governance/contracts/src/RiscZeroGovernorCounting.sol @@ -0,0 +1,192 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.8.9; + +import {Governor} from "openzeppelin/contracts/governance/Governor.sol"; + +/** + * @dev Implementation of of vote counting for the BonsaiGovernor, based on GovernorCountingSimple. + */ +abstract contract RiscZeroGovernorCounting is Governor { + /// @notice Emitted when a ballot is committed to the ballot box. + /// @param encoded ballot encoded as bytes exactly as included in the hash. + event CommittedBallot(uint256 indexed proposalId, bytes encoded); + + /** + * @dev Supported vote types. Matches Governor Bravo ordering. + */ + enum VoteType { + Against, + For, + Abstain + } + + struct ProposalVote { + bool finalized; + bytes32 ballotBoxCommit; + uint256 againstVotes; + uint256 forVotes; + uint256 abstainVotes; + } + + mapping(uint256 => ProposalVote) private _proposalVotes; + + /** + * @dev See {IGovernor-COUNTING_MODE}. + */ + // solhint-disable-next-line func-name-mixedcase + function COUNTING_MODE() public pure virtual override returns (string memory) { + return "support=bravo&quorum=for,abstain"; + } + + /** + * @dev See {IGovernor-hasVoted}. + */ + function hasVoted(uint256, address) public pure override returns (bool) { + revert("hasVoted is not supported"); + } + + /** + * @dev Accessor to the internal vote counts. + */ + function proposalVotes(uint256 proposalId) + public + view + virtual + returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) + { + ProposalVote storage proposalVote = _proposalVotes[proposalId]; + require(proposalVote.finalized, "proposalVotes: votes have not been finalized"); + + return (proposalVote.againstVotes, proposalVote.forVotes, proposalVote.abstainVotes); + } + + /** + * @dev Accessor to the internal finalization status of the votes. + */ + function _proposalVotesFinalized(uint256 proposalId) internal view virtual returns (bool) { + return _proposalVotes[proposalId].finalized; + } + + /** + * @dev See {Governor-_quorumReached}. + */ + function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { + // This function is only used internally, and reverting here would break the current, + // implementation of the proposal state function, so this function will return for non-final + // results. If the result is not finalized, it will always be false. + ProposalVote storage proposalVote = _proposalVotes[proposalId]; + + return quorum(proposalSnapshot(proposalId)) <= proposalVote.forVotes + proposalVote.abstainVotes; + } + + /** + * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. + */ + function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { + // This function is only used internally, and reverting here would break the current, + // implementation of the proposal state function, so this function will return for non-final + // results. If the result is not finalized, it will always be false. + ProposalVote storage proposalVote = _proposalVotes[proposalId]; + + return proposalVote.forVotes > proposalVote.againstVotes; + } + + /** + * @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo). + */ + function _countVote(uint256, address, uint8, uint256, bytes memory) internal pure override { + revert("_countVote is not supported"); + } + + /// @notice Commits the signed vote to the proposal by hashing it into the accumulator. + function _commitVoteBySig(uint256 proposalId, uint8 support, bytes memory signature, bytes32 sigDigest) internal { + ProposalVote storage proposalVote = _proposalVotes[proposalId]; + + (bytes32 r, bytes32 s, uint8 v) = abi.decode(signature, (bytes32, bytes32, uint8)); + + // Hash into the ballot bx the 68-byte encoded ballot with signature. + // NOTE: Fields are aligned with 4 and 32 bytes boundaries. + bytes memory encoded = abi.encodePacked(uint16(1), support, v, r, s, sigDigest); + + emit CommittedBallot(proposalId, encoded); + proposalVote.ballotBoxCommit = sha256(bytes.concat(proposalVote.ballotBoxCommit, encoded)); + } + + /// @notice Commits the on-chain vote to the proposal ballot box by hashing it into the accumulator. + function _commitVote(uint256 proposalId, uint8 support, address account) internal { + ProposalVote storage proposalVote = _proposalVotes[proposalId]; + + // Hash into the ballot bx the 24-byte encoded ballot without a signature. + bytes memory encoded = abi.encodePacked(uint16(0), support, uint8(0), account); + + emit CommittedBallot(proposalId, encoded); + proposalVote.ballotBoxCommit = sha256(bytes.concat(proposalVote.ballotBoxCommit, encoded)); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override returns (uint256) { + uint256 id = super.propose(targets, values, calldatas, description); + // Write the proposal id to the ballot box commitment to act as a salt. + _proposalVotes[id].ballotBoxCommit = bytes32(id); + return id; + } + + function _finalizeVotes(uint256 proposalId, bytes32 ballotHash, bytes calldata votingData) internal { + require(clock() > proposalDeadline(proposalId), "voting has not ended"); + ProposalVote memory proposalVote = _proposalVotes[proposalId]; + require(!proposalVote.finalized, "votes have already been finalized"); + require(proposalVote.ballotBoxCommit == ballotHash, "ballot box accumulator mismatch"); + + require(votingData.length % 24 == 0, "must encode a whole number of encoded ballots"); + + proposalVote.finalized = true; + + uint256 snapshot = proposalSnapshot(proposalId); + bytes memory params = _defaultParams(); + + // Iterate through the encoded ballots in chunks of 24 bytes. + for (uint256 offset = 0; offset < votingData.length; offset = offset + 24) { + // Decode the packed ballot encoding. + // { uint16(0), uint8(support), uint8(0), address } + bytes24 ballot = bytes24(votingData[offset:offset + 24]); + uint8 support = uint8(uint192(ballot) >> 168); + address account = address(uint160(uint192(ballot))); + + // Look up the voting weight for the account associated with the ballot. + // NOTE: If a signature was invalid, it will likely result in a lookup for a random + // account, which will almost certainly has zero voting weight. + uint256 weight = _getVotes(account, snapshot, params); + if (support == uint8(VoteType.Against)) { + proposalVote.againstVotes += weight; + } else if (support == uint8(VoteType.For)) { + proposalVote.forVotes += weight; + } else if (support == uint8(VoteType.Abstain)) { + proposalVote.abstainVotes += weight; + } else { + revert("RiscZeroGovernorCounting: invalid value for enum VoteType"); + } + } + + // Write the final tally results back to storage. + _proposalVotes[proposalId] = proposalVote; + } +} diff --git a/examples/governance/contracts/src/VoteToken.sol b/examples/governance/contracts/src/VoteToken.sol new file mode 100644 index 00000000..638cdb7e --- /dev/null +++ b/examples/governance/contracts/src/VoteToken.sol @@ -0,0 +1,39 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.8.9; + +import "openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "openzeppelin/contracts/access/Ownable.sol"; +import "openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; +import "openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; + +/// @custom:security-contact security@risczero.com +contract VoteToken is ERC20, Ownable, ERC20Permit, ERC20Votes { + constructor() ERC20("VoteToken", "VTK") ERC20Permit("VoteToken") Ownable(msg.sender) {} + + function mint(address to, uint256 amount) public onlyOwner { + _mint(to, amount); + } + + function _update(address from, address to, uint256 value) internal override(ERC20, ERC20Votes) { + super._update(from, to, value); + } + + function nonces(address owner) public view override(Nonces, ERC20Permit) returns (uint256) { + return super.nonces(owner); + } +} diff --git a/examples/governance/contracts/test/BaselineGovernorTest.sol b/examples/governance/contracts/test/BaselineGovernorTest.sol new file mode 100644 index 00000000..cd2725ad --- /dev/null +++ b/examples/governance/contracts/test/BaselineGovernorTest.sol @@ -0,0 +1,167 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.9; + +import {Test} from "forge-std/Test.sol"; +import {GovernorTestBase} from "./GovernorTestBase.sol"; +import {console2} from "forge-std/console2.sol"; +import {BaselineGovernor} from "../src/BaselineGovernor.sol"; +import {VoteToken} from "../src/VoteToken.sol"; +import {IGovernor} from "openzeppelin/contracts/governance/IGovernor.sol"; + +contract BaselineGovernorTest is Test, GovernorTestBase { + function setUp() public override { + super.setUp(); + } + + function testProposalCreation() public { + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + + uint256 proposalId = baselineGovernor.propose(targets, values, calldatas, description); + + assertGt(proposalId, 0, "Proposal should be created with non-zero ID"); + assertEq( + uint256(baselineGovernor.state(proposalId)), + uint256(IGovernor.ProposalState.Pending), + "New proposal should be in Pending state" + ); + } + + function testProposalIDs() public { + // Try to create a proposal with Charlie (who has 30 tokens) + vm.prank(charlie); + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + uint256 proposalId = baselineGovernor.propose(targets, values, calldatas, description); + assertGt(proposalId, 0, "Proposal should be created with non-zero ID"); + + // Now try with Alice (who has 100 tokens) with different parameters + vm.prank(alice); + (address[] memory targets2, uint256[] memory values2, bytes[] memory calldatas2, string memory description2) = + _createProposalParams(); + targets2[0] = address(0x5); // Change target address + description2 = "Do something else"; // Change description + uint256 proposalId2 = baselineGovernor.propose(targets2, values2, calldatas2, description2); + assertGt(proposalId2, 0, "Second proposal should be created with non-zero ID"); + assertNotEq(proposalId, proposalId2, "Proposal IDs should be different"); + } + + function testVoting() public { + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + uint256 proposalId = baselineGovernor.propose(targets, values, calldatas, description); + + // Move to active state + vm.roll(block.number + baselineGovernor.votingDelay() + 1); + + vm.prank(alice); + baselineGovernor.castVote(proposalId, 1); // Vote in favor + + vm.prank(bob); + baselineGovernor.castVote(proposalId, 0); // Vote against + + (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) = baselineGovernor.proposalVotes(proposalId); + assertEq(forVotes, 100, "For votes should be 100"); + assertEq(againstVotes, 50, "Against votes should be 50"); + assertEq(abstainVotes, 0, "Abstain votes should be 0"); + } + + function testVotingBySignature() public { + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + + uint256 proposalId = baselineGovernor.propose(targets, values, calldatas, description); + + // Transfer tokens from alice to the new voter address + vm.prank(alice); + voteToken.transfer(voterAddress, 100); + + // Delegate voting power + vm.prank(voterAddress); + voteToken.delegate(voterAddress); + + // Move to active state + vm.roll(block.number + baselineGovernor.votingDelay() + 1); + + // Prepare vote data + uint8 support = 1; // 1 for 'For' + + // Generate the vote hash + bytes32 digest = baselineGovernor.voteHash(proposalId, support, voterAddress); + + // Sign the vote hash + (uint8 v, bytes32 r, bytes32 s) = vm.sign(voterPk, digest); + bytes memory signature = abi.encodePacked(r, s, v); + + // Cast vote by signature + baselineGovernor.castVoteBySig(proposalId, support, voterAddress, signature); + + // Check the vote was counted + (, uint256 forVotes,) = baselineGovernor.proposalVotes(proposalId); + + assertEq(forVotes, 100, "For votes should be 100"); + } + + function testQuorumAndExecution() public { + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + uint256 proposalId = baselineGovernor.propose(targets, values, calldatas, description); + + // Move to active state and vote + vm.roll(block.number + baselineGovernor.votingDelay() + 1); + vm.prank(alice); + baselineGovernor.castVote(proposalId, 1); + vm.prank(bob); + baselineGovernor.castVote(proposalId, 1); + + // Move to end of voting period + vm.roll(block.number + baselineGovernor.votingPeriod() + 1); + + assertEq( + uint256(baselineGovernor.state(proposalId)), + uint256(IGovernor.ProposalState.Succeeded), + "Proposal should have succeeded" + ); + + baselineGovernor.execute(targets, values, calldatas, keccak256(bytes(description))); + assertEq( + uint256(baselineGovernor.state(proposalId)), + uint256(IGovernor.ProposalState.Executed), + "Proposal should be executed" + ); + } + + function testQuorumNotReached() public { + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + uint256 proposalId = baselineGovernor.propose(targets, values, calldatas, description); + + // Move to active state and vote + vm.roll(block.number + baselineGovernor.votingDelay() + 1); + vm.prank(charlie); + baselineGovernor.castVote(proposalId, 1); + + // Move to end of voting period + vm.roll(block.number + baselineGovernor.votingPeriod() + 1); + + assertEq( + uint256(baselineGovernor.state(proposalId)), + uint256(IGovernor.ProposalState.Defeated), + "Proposal should be defeated due to not reaching quorum" + ); + } +} diff --git a/examples/governance/contracts/test/GovernorTestBase.sol b/examples/governance/contracts/test/GovernorTestBase.sol new file mode 100644 index 00000000..f47e4cdf --- /dev/null +++ b/examples/governance/contracts/test/GovernorTestBase.sol @@ -0,0 +1,111 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.9; + +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import {BaselineGovernor} from "../src/BaselineGovernor.sol"; +import {RiscZeroGovernor} from "../src/RiscZeroGovernor.sol"; +import {VoteToken} from "../src/VoteToken.sol"; +import {IGovernor} from "openzeppelin/contracts/governance/IGovernor.sol"; +import {ImageID} from "../src/ImageID.sol"; +import {RiscZeroMockVerifier, Receipt as VerifierReceipt} from "risc0/test/RiscZeroMockVerifier.sol"; +import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol"; + +contract GovernorTestBase is Test { + BaselineGovernor public baselineGovernor; + RiscZeroGovernor public riscZeroGovernor; + VoteToken public voteToken; + RiscZeroMockVerifier public mockVerifier; + + address public alice; + address public bob; + address public charlie; + address public voterAddress; + uint256 public voterPk; + + bytes32 public constant IMAGE_ID = ImageID.FINALIZE_VOTES_ID; + bytes4 public constant MOCK_SELECTOR = bytes4(uint32(1337)); + + event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason); + + function setUp() public virtual { + voteToken = new VoteToken(); + mockVerifier = new RiscZeroMockVerifier(MOCK_SELECTOR); + baselineGovernor = new BaselineGovernor(voteToken); + riscZeroGovernor = new RiscZeroGovernor(voteToken, IMAGE_ID, mockVerifier); + + alice = vm.addr(1); + bob = vm.addr(2); + charlie = vm.addr(3); + + voteToken.mint(alice, 100); + voteToken.mint(bob, 50); + voteToken.mint(charlie, 30); + + vm.prank(alice); + voteToken.delegate(alice); + vm.prank(bob); + voteToken.delegate(bob); + vm.prank(charlie); + voteToken.delegate(charlie); + + (voterAddress, voterPk) = makeAddrAndKey("voter"); + } + + function getSignature(uint8 support, uint256 proposalId, address signer, uint256 privateKey) + internal + returns (bytes memory signature) + { + bytes32 digest = baselineGovernor.voteHash(proposalId, support, signer); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + signature = abi.encodePacked(r, s, v); + } + + // this function replicates the behavior of _commitVote in RiscZeroGovernorCounting.sol + function hashBallots(uint8 aliceSupport, uint8 bobSupport, uint256 proposalId) + internal + view + returns (bytes32, bytes memory) + { + bytes memory encodeAliceVote = abi.encodePacked(uint16(0), aliceSupport, uint8(0), alice); + bytes memory encodeBobVote = abi.encodePacked(uint16(0), bobSupport, uint8(0), bob); + bytes memory encodedBallots = bytes.concat(encodeAliceVote, encodeBobVote); + + // initial salt for accumulator from `propose` in `GovernorCounting` + bytes32 ballotBoxAccum = bytes32(proposalId); + ballotBoxAccum = sha256(bytes.concat(ballotBoxAccum, encodeAliceVote)); + bytes32 finalBallotBoxAccum = sha256(bytes.concat(ballotBoxAccum, encodeBobVote)); + + return (finalBallotBoxAccum, encodedBallots); + } + + function _createProposalParams() + internal + pure + returns (address[] memory, uint256[] memory, bytes[] memory, string memory) + { + address[] memory targets = new address[](1); + targets[0] = address(0x4); + uint256[] memory values = new uint256[](1); + values[0] = 0; + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSignature("doSomething()"); + string memory description = "Do something"; + + return (targets, values, calldatas, description); + } +} diff --git a/examples/governance/contracts/test/RiscZeroGovernorTest.sol b/examples/governance/contracts/test/RiscZeroGovernorTest.sol new file mode 100644 index 00000000..470c0503 --- /dev/null +++ b/examples/governance/contracts/test/RiscZeroGovernorTest.sol @@ -0,0 +1,248 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.9; + +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import {GovernorTestBase} from "./GovernorTestBase.sol"; +import {RiscZeroGovernor} from "../src/RiscZeroGovernor.sol"; +import {VoteToken} from "../src/VoteToken.sol"; +import {IGovernor} from "openzeppelin/contracts/governance/IGovernor.sol"; +import {ImageID} from "../src/ImageID.sol"; +import {RiscZeroMockVerifier, Receipt as VerifierReceipt} from "risc0/test/RiscZeroMockVerifier.sol"; +import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol"; + +contract RiscZeroGovernorTest is Test, GovernorTestBase { + uint8 public aliceSupport; + uint8 public bobSupport; + uint8 public charlieSupport; + + event CommittedBallot(uint256 indexed proposalId, bytes encoded); + + struct ProposalVote { + bool finalized; + bytes32 ballotBoxCommit; + uint256 againstVotes; + uint256 forVotes; + uint256 abstainVotes; + } + + function setUp() public override { + super.setUp(); + } + + function testProposalCreation() public { + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + + uint256 proposalId = riscZeroGovernor.propose(targets, values, calldatas, description); + + assertGt(proposalId, 0, "Proposal should be created with non-zero ID"); + assertEq( + uint256(riscZeroGovernor.state(proposalId)), + uint256(IGovernor.ProposalState.Pending), + "New proposal should be in Pending state" + ); + } + + function testVoting() public { + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + uint256 proposalId = riscZeroGovernor.propose(targets, values, calldatas, description); + + vm.roll(block.number + riscZeroGovernor.votingDelay() + 1); + + aliceSupport = 1; // Vote in favor + vm.prank(alice); + vm.expectEmit(); + emit VoteCast(alice, proposalId, aliceSupport, 0, ""); + riscZeroGovernor.castVote(proposalId, aliceSupport); + + bobSupport = 0; // Vote against + vm.prank(bob); + vm.expectEmit(); + emit VoteCast(bob, proposalId, bobSupport, 0, ""); + riscZeroGovernor.castVote(proposalId, bobSupport); + } + + function testVotingBySignature() public { + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + uint256 proposalId = riscZeroGovernor.propose(targets, values, calldatas, description); + + // Transfer tokens from alice to the new voter address + vm.prank(alice); + voteToken.transfer(voterAddress, 100); + + // Delegate voting power + vm.prank(voterAddress); + voteToken.delegate(voterAddress); + + vm.roll(block.number + riscZeroGovernor.votingDelay() + 1); + + uint8 forSupport = 1; // 1 for 'For' + + bytes32 digest = riscZeroGovernor.voteHash(proposalId, forSupport, voterAddress); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(voterPk, digest); + bytes memory signature = abi.encode(r, s, v); + + bytes memory encoded = abi.encodePacked(uint16(1), forSupport, v, r, s, digest); + + vm.expectEmit(); + emit CommittedBallot(proposalId, encoded); + riscZeroGovernor.castVoteBySig(proposalId, forSupport, voterAddress, signature); + } + + function testVerifyAndFinalizeVotes() public { + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + + uint256 proposalId = riscZeroGovernor.propose(targets, values, calldatas, description); + + aliceSupport = 1; + bobSupport = 0; + + vm.roll(block.number + riscZeroGovernor.votingDelay() + 1); + + vm.prank(alice); + riscZeroGovernor.castVote(proposalId, aliceSupport); + + vm.prank(bob); + riscZeroGovernor.castVote(proposalId, bobSupport); + + vm.roll(block.number + riscZeroGovernor.votingPeriod() + 1); + + (bytes32 finalBallotBoxAccum, bytes memory encodedBallots) = hashBallots(aliceSupport, bobSupport, proposalId); + + bytes memory journal = abi.encodePacked(proposalId, finalBallotBoxAccum, encodedBallots); + + // create mock receipt + bytes32 journalDigest = sha256(journal); + VerifierReceipt memory receipt = mockVerifier.mockProve(ImageID.FINALIZE_VOTES_ID, journalDigest); + + // mock call to verifier called in verifyAndFinalizeVotes() + address verifierAddress = riscZeroGovernor.verifier.address; + bytes4 verifySelector = IRiscZeroVerifier.verify.selector; + bytes memory expectedCalldata = abi.encodeWithSelector(verifySelector, receipt.seal, IMAGE_ID, journalDigest); + + vm.mockCall(verifierAddress, expectedCalldata, abi.encode()); + + riscZeroGovernor.verifyAndFinalizeVotes(receipt.seal, journal); + (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) = riscZeroGovernor.proposalVotes(proposalId); + + assertEq(forVotes, 100, "For votes should be 100"); + assertEq(againstVotes, 50, "Against votes should be 50"); + assertEq(abstainVotes, 0, "Abstain votes should be 0"); + + vm.clearMockedCalls(); + } + + function testQuorumAndExecution() public { + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + + uint256 proposalId = riscZeroGovernor.propose(targets, values, calldatas, description); + + aliceSupport = 1; + bobSupport = 1; + + vm.roll(block.number + riscZeroGovernor.votingDelay() + 1); + + // Cast votes + vm.prank(alice); + riscZeroGovernor.castVote(proposalId, 1); + vm.prank(bob); + riscZeroGovernor.castVote(proposalId, 1); + + vm.roll(block.number + riscZeroGovernor.votingPeriod() + 1); + + (bytes32 finalBallotBoxAccum, bytes memory encodedBallots) = hashBallots(aliceSupport, bobSupport, proposalId); + + bytes memory journal = abi.encodePacked(proposalId, finalBallotBoxAccum, encodedBallots); + bytes32 journalDigest = sha256(journal); + + VerifierReceipt memory receipt = mockVerifier.mockProve(ImageID.FINALIZE_VOTES_ID, journalDigest); + + // Mock the verifier call + address verifierAddress = riscZeroGovernor.verifier.address; + bytes4 verifySelector = IRiscZeroVerifier.verify.selector; + bytes memory expectedCalldata = abi.encodeWithSelector(verifySelector, receipt.seal, IMAGE_ID, journalDigest); + vm.mockCall(verifierAddress, expectedCalldata, abi.encode()); + + // Call verifyAndFinalizeVotes + riscZeroGovernor.verifyAndFinalizeVotes(receipt.seal, journal); + + assertEq( + uint256(riscZeroGovernor.state(proposalId)), + uint256(IGovernor.ProposalState.Succeeded), + "Proposal should have succeeded" + ); + + riscZeroGovernor.execute(targets, values, calldatas, keccak256(bytes(description))); + assertEq( + uint256(riscZeroGovernor.state(proposalId)), + uint256(IGovernor.ProposalState.Executed), + "Proposal should be executed" + ); + + vm.clearMockedCalls(); + } + + function testQuorumNotReached() public { + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + uint256 proposalId = riscZeroGovernor.propose(targets, values, calldatas, description); + + charlieSupport = 1; + + vm.roll(block.number + riscZeroGovernor.votingDelay() + 1); + + // Only Bob votes, which isn't enough to reach quorum + vm.prank(charlie); + riscZeroGovernor.castVote(proposalId, 1); + + vm.roll(block.number + riscZeroGovernor.votingPeriod() + 1); + + bytes memory encodeCharlieVote = abi.encodePacked(uint16(0), charlieSupport, uint8(0), charlie); + bytes memory encodedBallots = encodeCharlieVote; + + // hashBallots supports only two votes + // so we recreate it just with one vote from Charlie here + bytes32 ballotBoxAccum = bytes32(proposalId); + bytes32 finalBallotBoxAccum = sha256(bytes.concat(ballotBoxAccum, encodeCharlieVote)); + + bytes memory journal = abi.encodePacked(proposalId, finalBallotBoxAccum, encodedBallots); + bytes32 journalDigest = sha256(journal); + + VerifierReceipt memory receipt = mockVerifier.mockProve(ImageID.FINALIZE_VOTES_ID, journalDigest); + + // Mock the verifier call + address verifierAddress = riscZeroGovernor.verifier.address; + bytes4 verifySelector = IRiscZeroVerifier.verify.selector; + bytes memory expectedCalldata = abi.encodeWithSelector(verifySelector, receipt.seal, IMAGE_ID, journalDigest); + vm.mockCall(verifierAddress, expectedCalldata, abi.encode()); + + // Call verifyAndFinalizeVotes + riscZeroGovernor.verifyAndFinalizeVotes(receipt.seal, journal); + + assertEq( + uint256(riscZeroGovernor.state(proposalId)), + uint256(IGovernor.ProposalState.Defeated), + "Proposal should be defeated due to not reaching quorum" + ); + } +} diff --git a/examples/governance/contracts/test/benchmarks/BenchmarkGovernorsTest.sol b/examples/governance/contracts/test/benchmarks/BenchmarkGovernorsTest.sol new file mode 100644 index 00000000..7f3e9f48 --- /dev/null +++ b/examples/governance/contracts/test/benchmarks/BenchmarkGovernorsTest.sol @@ -0,0 +1,160 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.9; + +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import {BaselineGovernor} from "../../src/BaselineGovernor.sol"; +import {RiscZeroGovernor} from "../../src/RiscZeroGovernor.sol"; +import {BenchmarkTestBase} from "./BenchmarkTestBase.sol"; +import {VoteToken} from "../../src/VoteToken.sol"; +import {IGovernor} from "openzeppelin/contracts/governance/IGovernor.sol"; +import {Strings} from "openzeppelin/contracts/utils/Strings.sol"; +import {ImageID} from "../../src/ImageID.sol"; +import {RiscZeroMockVerifier, Receipt as VerifierReceipt} from "risc0/test/RiscZeroMockVerifier.sol"; +import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol"; + +contract BenchmarkGovernorsTest is Test, BenchmarkTestBase { + function setUp() public override { + super.setUp(); + } + + function testFuzz_BaselineWorkflow(uint16 numAccounts) public { + vm.pauseGasMetering(); + numAccounts = uint16(bound(numAccounts, 100, 1000)); + + // Generate accounts and mint tokens + generateAccounts(numAccounts); + for (uint256 i = 0; i < numAccounts; i++) { + address currentAddress = accounts[i]; + voteToken.mint(currentAddress, 100); + vm.prank(currentAddress); + voteToken.delegate(currentAddress); + } + vm.resumeGasMetering(); + + // metering for output + uint256 gasUsed = gasleft(); + + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + + baselineProposalId = baselineGovernor.propose(targets, values, calldatas, description); + + generateSignatures(baselineProposalId, numAccounts, true); + + // move to active voting period + vm.roll(block.number + baselineGovernor.votingDelay() + 1); + + // cast votes + uint256 expectedForVotes = 0; + for (uint256 i = 0; i < numAccounts; i++) { + baselineGovernor.castVoteBySig(baselineProposalId, forSupport, accounts[i], baselineSignatures[accounts[i]]); + + expectedForVotes += 100; // Each account has 100 voting power + vm.roll(block.number + 1); + } + + // Move to end of voting period + vm.roll(block.number + baselineGovernor.votingPeriod() + 1); + + // execute proposal + baselineGovernor.execute(targets, values, calldatas, keccak256(bytes(description))); + + gasUsed = gasUsed - gasleft(); + vm.writeLine( + "contracts/test/benchmarks/gas_data.csv", + string(abi.encodePacked("BaselineGovernor,", vm.toString(numAccounts), ",", vm.toString(gasUsed))) + ); + + assertEq( + uint256(baselineGovernor.state(baselineProposalId)), + uint256(IGovernor.ProposalState.Executed), + "Proposal should be executed" + ); + } + + function testFuzz_RiscZeroWorkflow(uint16 numAccounts) public { + vm.pauseGasMetering(); + numAccounts = uint16(bound(numAccounts, 100, 1000)); + + // Generate accounts and mint tokens + generateAccounts(numAccounts); + for (uint256 i = 0; i < numAccounts; i++) { + address currentAddress = accounts[i]; + voteToken.mint(currentAddress, 100); + vm.prank(currentAddress); + voteToken.delegate(currentAddress); + } + vm.resumeGasMetering(); + + // metering for output + uint256 gasUsed = gasleft(); + + (address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) = + _createProposalParams(); + + uint256 proposalId = riscZeroGovernor.propose(targets, values, calldatas, description); + + // move to active voting period + vm.roll(block.number + riscZeroGovernor.votingDelay() + 1); + + // cast votes + for (uint256 i = 0; i < numAccounts; i++) { + vm.prank(accounts[i]); + riscZeroGovernor.castVote(proposalId, forSupport); + vm.roll(block.number + 1); + } + + // get journal digest + bytes32 journalDigest = getJournalDigest(proposalId, numAccounts); + + // mock prove + VerifierReceipt memory receipt = mockVerifier.mockProve(ImageID.FINALIZE_VOTES_ID, journalDigest); + + // mock call to verifier called in verifyAndFinalizeVotes() + address verifierAddress = riscZeroGovernor.verifier.address; + bytes memory expectedCalldata = + abi.encodeWithSelector(IRiscZeroVerifier.verify.selector, receipt.seal, IMAGE_ID, journalDigest); + + vm.mockCall(verifierAddress, expectedCalldata, abi.encode()); + + // Move to end of voting period + vm.roll(block.number + riscZeroGovernor.votingPeriod() + 1); + + // call `verifyAndFinalizeVotes` and assert expectations + bytes memory journal = getJournal(proposalId, numAccounts); + riscZeroGovernor.verifyAndFinalizeVotes(receipt.seal, journal); + + // Offsetting gas by real verification cost for more accurate benchmarks + gasUsed += 250000; + + // execute proposal + riscZeroGovernor.execute(targets, values, calldatas, keccak256(bytes(description))); + + gasUsed = gasUsed - gasleft(); + vm.writeLine( + "contracts/test/benchmarks/gas_data.csv", + string(abi.encodePacked("RiscZeroGovernor,", vm.toString(numAccounts), ",", vm.toString(gasUsed))) + ); + + assertEq( + uint256(riscZeroGovernor.state(proposalId)), + uint256(IGovernor.ProposalState.Executed), + "Proposal should be executed" + ); + } +} diff --git a/examples/governance/contracts/test/benchmarks/BenchmarkTestBase.sol b/examples/governance/contracts/test/benchmarks/BenchmarkTestBase.sol new file mode 100644 index 00000000..09643422 --- /dev/null +++ b/examples/governance/contracts/test/benchmarks/BenchmarkTestBase.sol @@ -0,0 +1,140 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.9; + +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import {BaselineGovernor} from "../../src/BaselineGovernor.sol"; +import {RiscZeroGovernor} from "../../src/RiscZeroGovernor.sol"; +import {VoteToken} from "../../src/VoteToken.sol"; +import {IGovernor} from "openzeppelin/contracts/governance/IGovernor.sol"; +import {Strings} from "openzeppelin/contracts/utils/Strings.sol"; +import {ImageID} from "../../src/ImageID.sol"; +import {RiscZeroMockVerifier} from "risc0/test/RiscZeroMockVerifier.sol"; +import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol"; + +contract BenchmarkTestBase is Test { + BaselineGovernor public baselineGovernor; + RiscZeroGovernor public riscZeroGovernor; + VoteToken public voteToken; + RiscZeroMockVerifier public mockVerifier; + + uint8 public forSupport = 1; + address[] public accounts; + uint256 public baselineProposalId; + uint256 public riscZeroProposalId; + mapping(address => bytes) public baselineSignatures; + mapping(address => bytes) public riscZeroSignatures; + mapping(address => uint256) public keys; + bytes32 public constant IMAGE_ID = ImageID.FINALIZE_VOTES_ID; + bytes4 public constant MOCK_SELECTOR = bytes4(uint32(1337)); + + function setUp() public virtual { + voteToken = new VoteToken(); + mockVerifier = new RiscZeroMockVerifier(MOCK_SELECTOR); + baselineGovernor = new BaselineGovernor(voteToken); + riscZeroGovernor = new RiscZeroGovernor(voteToken, IMAGE_ID, mockVerifier); + } + + function generateAccounts(uint256 noOfAccounts) public noGasMetering { + for (uint256 i = 0; i < noOfAccounts; i++) { + string memory base = string(abi.encodePacked("yolo", Strings.toString(i))); + (address tempAddress, uint256 tempPk) = makeAddrAndKey(base); + accounts.push(tempAddress); + keys[tempAddress] = tempPk; + } + } + + function getSignature(uint8 support, uint256 proposalId, address signer, uint256 privateKey) + internal + noGasMetering + returns (bytes memory signature) + { + bytes32 digest = baselineGovernor.voteHash(proposalId, support, signer); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + signature = abi.encodePacked(r, s, v); + } + + function generateSignatures(uint256 proposalId, uint256 noOfSignatures, bool isBaseline) internal noGasMetering { + for (uint256 i = 0; i < noOfSignatures; i++) { + address currentAddress = accounts[i]; + bytes memory signature = getSignature(forSupport, proposalId, currentAddress, keys[currentAddress]); + + if (isBaseline) { + baselineSignatures[currentAddress] = signature; + } else { + riscZeroSignatures[currentAddress] = signature; + } + } + } + + function getJournal(uint256 proposalId, uint256 noOfAccounts) + internal + noGasMetering + returns (bytes memory journal) + { + (bytes32 finalBallotBoxAccum, bytes memory encodedBallots) = hashBallots(noOfAccounts, proposalId); + + journal = abi.encodePacked(proposalId, finalBallotBoxAccum, encodedBallots); + } + + function getJournalDigest(uint256 proposalId, uint256 noOfAccounts) + internal + noGasMetering + returns (bytes32 journalDigest) + { + bytes memory journal = getJournal(proposalId, noOfAccounts); + journalDigest = sha256(journal); + } + + function hashBallots(uint256 noOfAccounts, uint256 proposalId) + internal + noGasMetering + returns (bytes32, bytes memory) + { + bytes memory encodedBallots; + bytes32 ballotHash = bytes32(proposalId); + + // Prepare the journal and receipt for verifyAndFinalizeVotes + for (uint256 i = 0; i < noOfAccounts; i++) { + address currentAddress = accounts[i]; + + bytes memory encodedVote = abi.encodePacked(uint16(0), forSupport, uint8(0), currentAddress); + + encodedBallots = bytes.concat(encodedBallots, encodedVote); + ballotHash = sha256(bytes.concat(ballotHash, encodedVote)); + } + + return (ballotHash, encodedBallots); + } + + function _createProposalParams() + internal + pure + returns (address[] memory, uint256[] memory, bytes[] memory, string memory) + { + address[] memory targets = new address[](1); + targets[0] = address(0x4); + uint256[] memory values = new uint256[](1); + values[0] = 0; + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSignature("doSomething()"); + string memory description = "Do something"; + + return (targets, values, calldatas, description); + } +} diff --git a/examples/governance/contracts/test/benchmarks/environment.yml b/examples/governance/contracts/test/benchmarks/environment.yml new file mode 100644 index 00000000..3ccfbec7 --- /dev/null +++ b/examples/governance/contracts/test/benchmarks/environment.yml @@ -0,0 +1,60 @@ +name: governance +channels: + - defaults +dependencies: + - blas=1.0=openblas + - brotli=1.0.9=h80987f9_8 + - brotli-bin=1.0.9=h80987f9_8 + - bzip2=1.0.8=h80987f9_6 + - ca-certificates=2024.7.2=hca03da5_0 + - contourpy=1.2.0=py312h48ca7d4_0 + - cycler=0.11.0=pyhd3eb1b0_0 + - expat=2.6.2=h313beb8_0 + - fonttools=4.51.0=py312h80987f9_0 + - freetype=2.12.1=h1192e45_0 + - jpeg=9e=h80987f9_3 + - kiwisolver=1.4.4=py312h313beb8_0 + - lcms2=2.12=hba8e193_0 + - lerc=3.0=hc377ac9_0 + - libbrotlicommon=1.0.9=h80987f9_8 + - libbrotlidec=1.0.9=h80987f9_8 + - libbrotlienc=1.0.9=h80987f9_8 + - libcxx=14.0.6=h848a8c0_0 + - libdeflate=1.17=h80987f9_1 + - libffi=3.4.4=hca03da5_1 + - libgfortran=5.0.0=11_3_0_hca03da5_28 + - libgfortran5=11.3.0=h009349e_28 + - libopenblas=0.3.21=h269037a_0 + - libpng=1.6.39=h80987f9_0 + - libtiff=4.5.1=h313beb8_0 + - libwebp-base=1.3.2=h80987f9_0 + - llvm-openmp=14.0.6=hc6e5704_0 + - lz4-c=1.9.4=h313beb8_1 + - matplotlib=3.8.4=py312hca03da5_0 + - matplotlib-base=3.8.4=py312hd77ebd4_0 + - ncurses=6.4=h313beb8_0 + - numpy=1.26.4=py312h7f4fdc5_0 + - numpy-base=1.26.4=py312he047099_0 + - openjpeg=2.5.2=h54b8e55_0 + - openssl=3.0.14=h80987f9_0 + - packaging=24.1=py312hca03da5_0 + - pillow=10.4.0=py312h80987f9_0 + - pip=24.2=py312hca03da5_0 + - prettytable=3.5.0=py312hca03da5_0 + - pyparsing=3.0.9=py312hca03da5_0 + - python=3.12.4=h99e199e_1 + - python-dateutil=2.9.0post0=py312hca03da5_2 + - readline=8.2=h1a28f6b_0 + - setuptools=72.1.0=py312hca03da5_0 + - six=1.16.0=pyhd3eb1b0_1 + - sqlite=3.45.3=h80987f9_0 + - tk=8.6.14=h6ba3021_0 + - tornado=6.4.1=py312h80987f9_0 + - tzdata=2024a=h04d1e81_0 + - unicodedata2=15.1.0=py312h80987f9_0 + - wcwidth=0.2.5=pyhd3eb1b0_0 + - wheel=0.43.0=py312hca03da5_0 + - xz=5.4.6=h80987f9_1 + - zlib=1.2.13=h18a0788_1 + - zstd=1.5.5=hd90d995_2 +prefix: /opt/homebrew/anaconda3/envs/governance diff --git a/examples/governance/contracts/test/benchmarks/gas_data_comparison.png b/examples/governance/contracts/test/benchmarks/gas_data_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..b12a3a7473ce9b515e2364c511c4858071f62649 GIT binary patch literal 235171 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
0E=SLodNOz533Hl;}BkeFLi-?KlrJ-)C))xzJ&M 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
    EsP1=M?b#Yd}(8iM~> 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 E0YR(~x&QzG literal 0 HcmV?d00001 diff --git a/examples/governance/contracts/test/benchmarks/plot_gas_data.py b/examples/governance/contracts/test/benchmarks/plot_gas_data.py new file mode 100644 index 00000000..e7c9e1f9 --- /dev/null +++ b/examples/governance/contracts/test/benchmarks/plot_gas_data.py @@ -0,0 +1,87 @@ +import csv +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.ticker import ScalarFormatter, NullFormatter + +def plot_gas_data(filename): + baseline_data = {} + risczero_data = {} + + # Read the CSV file + with open(filename, 'r') as f: + reader = csv.reader(f) + for row in reader: + governor, accounts, gas = row + accounts = int(accounts) + gas = int(gas) + if governor == 'BaselineGovernor': + baseline_data[accounts] = gas + else: + risczero_data[accounts] = gas + + # Sort the data + baseline_sorted = sorted(baseline_data.items()) + risczero_sorted = sorted(risczero_data.items()) + + # Separate x and y for plotting + baseline_x, baseline_y = zip(*baseline_sorted) + risczero_x, risczero_y = zip(*risczero_sorted) + + # Calculate average gas savings + common_x = set(baseline_x) & set(risczero_x) + savings = [(baseline_data[x] - risczero_data[x]) / baseline_data[x] * 100 for x in common_x] + avg_savings = np.mean(savings) + + # Create the plot + fig, ax = plt.subplots(figsize=(12, 8)) + ax.plot(baseline_x, baseline_y, label='BaselineGovernor', marker='o', linestyle='-', linewidth=2) + ax.plot(risczero_x, risczero_y, label='RiscZeroGovernor', marker='s', linestyle='--', linewidth=2) + + ax.set_xlabel('Number of Votes') + ax.set_ylabel('Gas Spent') + ax.set_title('Gas Data Comparison: BaselineGovernor vs RiscZeroGovernor') + + # Move legend to upper left + ax.legend(loc='upper left') + + ax.grid(True, which="both", ls="-", alpha=0.2) + + # Use logarithmic scale for better visibility + ax.set_xscale('log') + ax.set_yscale('log') + + # Customize x-axis + ax.xaxis.set_major_formatter(ScalarFormatter()) + ax.xaxis.set_minor_formatter(NullFormatter()) + + # Customize y-axis + ax.yaxis.set_major_formatter(ScalarFormatter(useMathText=True)) + ax.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + + # Add some key data points with adjusted positions + for x in [100, 500, 1000]: + if x in baseline_data and x in risczero_data: + # BaselineGovernor label + ax.annotate(f'({x}, {baseline_data[x]:,})', + xy=(x, baseline_data[x]), xytext=(5, -20), + textcoords='offset points', fontsize=8, + arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0')) + + # RiscZeroGovernor label (now also moved down) + ax.annotate(f'({x}, {risczero_data[x]:,})', + xy=(x, risczero_data[x]), xytext=(5, -15), + textcoords='offset points', fontsize=8, + arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0')) + + # Add summary statistics + stats_text = f'Average Gas Savings: {avg_savings:.2f}%' + ax.text(0.95, 0.05, stats_text, transform=ax.transAxes, fontsize=10, + verticalalignment='bottom', horizontalalignment='right', + bbox=dict(boxstyle='round', facecolor='white', alpha=0.8)) + + # Save the plot as a PNG file (removed '_improved') + plt.savefig('gas_data_comparison.png', dpi=300, bbox_inches='tight') + print("Plot saved as gas_data_comparison.png") + +# Usage +plot_gas_data('gas_data.csv') diff --git a/examples/governance/contracts/test/benchmarks/print_gas_data.py b/examples/governance/contracts/test/benchmarks/print_gas_data.py new file mode 100644 index 00000000..7ff1a899 --- /dev/null +++ b/examples/governance/contracts/test/benchmarks/print_gas_data.py @@ -0,0 +1,52 @@ +import csv +from prettytable import PrettyTable + +def print_nice_table(filename): + baseline_data = {} + risczero_data = {} + + # Read the CSV file + with open(filename, 'r') as f: + reader = csv.reader(f) + for row in reader: + governor, accounts, gas = row + accounts = int(accounts) + gas = int(gas) + if governor == 'BaselineGovernor': + baseline_data[accounts] = gas + else: + risczero_data[accounts] = gas + + # Create a sorted list of all unique account numbers + all_accounts = sorted(set(baseline_data.keys()) | set(risczero_data.keys())) + + # Create and populate the table + table = PrettyTable() + table.field_names = ["Votes", "BaselineGovernor Gas", "RiscZeroGovernor Gas", "Gas Savings", "% Savings"] + table.align["Votes"] = "r" + table.align["BaselineGovernor Gas"] = "r" + table.align["RiscZeroGovernor Gas"] = "r" + table.align["Gas Savings"] = "r" + table.align["% Savings"] = "r" + + for accounts in all_accounts: + baseline_gas = baseline_data.get(accounts, "N/A") + risczero_gas = risczero_data.get(accounts, "N/A") + + if baseline_gas != "N/A" and risczero_gas != "N/A": + gas_savings = baseline_gas - risczero_gas + percent_savings = (gas_savings / baseline_gas) * 100 + table.add_row([ + accounts, + f"{baseline_gas:,}", + f"{risczero_gas:,}", + f"{gas_savings:,}", + f"{percent_savings:.2f}%" + ]) + else: + table.add_row([accounts, baseline_gas, risczero_gas, "N/A", "N/A"]) + + print(table) + +# Usage +print_nice_table('gas_data.csv') diff --git a/examples/governance/contracts/test/benchmarks/requirements.txt b/examples/governance/contracts/test/benchmarks/requirements.txt new file mode 100644 index 00000000..cdd8d033 --- /dev/null +++ b/examples/governance/contracts/test/benchmarks/requirements.txt @@ -0,0 +1,18 @@ +contourpy==1.2.0 +cycler==0.11.0 +fonttools==4.51.0 +kiwisolver==1.4.4 +matplotlib==3.8.4 +numpy==1.26.4 +packaging==24.1 +pillow==10.4.0 +pip==24.2 +prettytable==3.5.0 +pyparsing==3.0.9 +python-dateutil==2.9.0.post0 +setuptools==72.1.0 +six==1.16.0 +tornado==6.4.1 +unicodedata2==15.1.0 +wcwidth==0.2.5 +wheel==0.43.0 diff --git a/examples/governance/foundry.toml b/examples/governance/foundry.toml new file mode 100644 index 00000000..074d67ee --- /dev/null +++ b/examples/governance/foundry.toml @@ -0,0 +1,13 @@ +[profile.default] +src = "contracts/src" +out = "out" +libs = ["../../lib", "../../contracts/src"] +test = "contracts/test" +script = "contracts/script" +ffi = true +fs_permissions = [{ access = "read-write", path = "./contracts/test/benchmarks"}] + +[fuzz] +runs = 10 +seed = '0x1234' + diff --git a/examples/governance/instructions.md b/examples/governance/instructions.md new file mode 100644 index 00000000..f97ca062 --- /dev/null +++ b/examples/governance/instructions.md @@ -0,0 +1,145 @@ +# Running this example locally + +Before starting, make sure to follow the [Installation guide][install-risc0] to have both Rust and RISC Zero's toolchain installed. You'll also need [Foundry][install-foundry] to be installed. + +## Setup + +From the root of the example repo, run: + +```sh +cargo build +forge install +forge build +``` + +## Running tests + +> [!TIP] +> You can adjust the number of fuzz runs for each test case, by changing the `runs` variable in [foundry.toml](./foundry.toml). Higher values gives more confidence in results at the cost of testing speed, see more info at [Fuzz - Foundry Docs][foundry-fuzz-docs]. + +```sh +RISC0_DEV_MODE=true forge test -vvv +``` + +Once completed, you should see some output similar to below. + +```sh +RISC0_DEV_MODE=true forge test -vvv +[⠊] Compiling... +[⠒] Compiling 87 files with Solc 0.8.23 +[⠢] Solc 0.8.23 finished in 4.17s +Compiler run successful with warnings: + +Ran 6 tests for contracts/test/BaselineGovernorTest.sol:BaselineGovernorTest +[PASS] testProposalCreation() (gas: 63271) +[PASS] testProposalIDs() (gas: 116812) +[PASS] testQuorumAndExecution() (gas: 198325) +[PASS] testQuorumNotReached() (gas: 139481) +[PASS] testVoting() (gas: 186488) +[PASS] testVotingBySignature() (gas: 271969) +Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 5.56ms (5.12ms CPU time) + +Ran 6 tests for contracts/test/RiscZeroGovernorTest.sol:RiscZeroGovernorTest +[PASS] testProposalCreation() (gas: 85552) +[PASS] testQuorumAndExecution() (gas: 252004) +[PASS] testQuorumNotReached() (gas: 213880) +[PASS] testVerifyAndFinalizeVotes() (gas: 241588) +[PASS] testVoting() (gas: 131218) +[PASS] testVotingBySignature() (gas: 220831) +Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 5.54ms (6.44ms CPU time) + +Ran 2 tests for contracts/test/benchmarks/BenchmarkGovernorsTest.sol:BenchmarkGovernorsTest +[PASS] testFuzz_BaselineWorkflow(uint16) (runs: 10, μ: 120564172, ~: 140488694) +[PASS] testFuzz_RiscZeroWorkflow(uint16) (runs: 10, μ: 86093198, ~: 100443129) +Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.82s (3.46s CPU time) + +Ran 3 test suites in 1.83s (1.83s CPU time): 14 tests passed, 0 failed, 0 skipped (14 total tests) +``` + +## Generating data and gas plots + +In the `tests/benchmarks` folder, there are two Python files. They both handle data written to a csv from the [BenchmarkGovernorsTest.sol](./tests/benchmarks/BenchmarkGovernorsTest.sol) file using Foundry's writing data cheatcode. + +- [print_gas_data.py]: pretty prints data from `gas_data.csv` +- [plot_gas_data.py]: generates a matplotlib plot of gas usage in .png format: [gas_data_comparison.png] + +To set up your python environment, it is recommend to use a virtual environment such as `venv` or `conda`. + +### Using venv + +Change into the benchmarks directory with: + +```sh +cd tests/benchmarks +``` + +Create the virtual environment with: + +```sh +python -m venv governance +``` + +Activate the virtual environment with: + +```sh +source governance/bin/activate +``` + +Install the requirements with: + +```sh +pip install -r requirements.txt +``` + +### Using conda + +Change into the benchmarks directory with: + +```sh +cd tests/benchmarks +``` + +Create the virtual environment with: + +```sh +conda env create -f environment.yml +``` + +Activate the virtual environment with: + +```sh +conda activate governance +``` + +## Running the print/plot python files + +Change into the benchmarks directory with: + +```sh +cd tests/benchmarks +``` + +Make sure the correct environment is activated and all the dependencies installed either with [venv](#using-venv) or an alternative like [conda](#using-conda). + +The data is only generated when the tests are run, see [running tests]. + +```sh +python print_gas_data.py +``` + +```sh +python plot_gas_data.py +``` + +The plot generated will be saved as [gas_data_comparison.png]: + +![Gas Data Comparison](contracts/test/benchmarks/gas_data_comparison.png) + +[install-foundry]: https://book.getfoundry.sh/getting-started/installation +[foundry-fuzz-docs]: https://book.getfoundry.sh/reference/config/testing#fuzz +[gas_data_comparison.png]: ./contracts/test/benchmarks/gas_data_comparison.png +[install-risc0]: https://dev.risczero.com/api/zkvm/install +[print_gas_data.py]: ./contracts/test/benchmarks/print_gas_data.py +[plot_gas_data.py]: ./contracts/test/benchmarks/plot_gas_data.py +[running tests]: #running-tests + diff --git a/examples/governance/methods/Cargo.toml b/examples/governance/methods/Cargo.toml new file mode 100644 index 00000000..53ba1f43 --- /dev/null +++ b/examples/governance/methods/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "governance-methods" +version = { workspace = true } +edition = { workspace = true } + +[package.metadata.risc0] +methods = ["guest"] + +[build-dependencies] +risc0-build = { workspace = true } +risc0-build-ethereum = { workspace = true } +risc0-zkp = { workspace = true } + +[dev-dependencies] +alloy-primitives = { workspace = true } +alloy-sol-types = { workspace = true } +hex = { workspace = true } +risc0-zkvm = { workspace = true, features = ["client"] } diff --git a/examples/governance/methods/README.md b/examples/governance/methods/README.md new file mode 100644 index 00000000..bc4cbcd2 --- /dev/null +++ b/examples/governance/methods/README.md @@ -0,0 +1,11 @@ +## zkVM Methods + +This directory contains the [zkVM] portion for the RISC Zero Governance application. +To learn more about the code in this directory, we recommend [Guest Code 101] and the [Foundry Template]. + +[Guest Code 101]: https://dev.risczero.com/api/zkvm/guest-code-101 +[zkVM]: https://dev.risczero.com/zkvm +[Foundry Template]: https://github.com/risc0/risc0-foundry-template + + + diff --git a/examples/governance/methods/build.rs b/examples/governance/methods/build.rs new file mode 100644 index 00000000..e02e0d25 --- /dev/null +++ b/examples/governance/methods/build.rs @@ -0,0 +1,47 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{collections::HashMap, env}; + +use risc0_build::{embed_methods_with_options, DockerOptions, GuestOptions}; +use risc0_build_ethereum::generate_solidity_files; + +// Paths where the generated Solidity files will be written. +const SOLIDITY_IMAGE_ID_PATH: &str = "../contracts/src/ImageID.sol"; +const SOLIDITY_ELF_PATH: &str = "../contracts/src/Elf.sol"; + +fn main() { + // Builds can be made deterministic, and thereby reproducible, by using Docker to build the + // guest. Check the RISC0_USE_DOCKER variable and use Docker to build the guest if set. + println!("cargo:rerun-if-env-changed=RISC0_USE_DOCKER"); + let use_docker = env::var("RISC0_USE_DOCKER").ok().map(|_| DockerOptions { + root_dir: Some("../".into()), + }); + + // Generate Rust source files for the methods crate. + let guests = embed_methods_with_options(HashMap::from([( + "governance-guest", + GuestOptions { + features: Vec::new(), + use_docker, + }, + )])); + + // Generate Solidity source files for use with Forge. + let solidity_opts = risc0_build_ethereum::Options::default() + .with_image_id_sol_path(SOLIDITY_IMAGE_ID_PATH) + .with_elf_sol_path(SOLIDITY_ELF_PATH); + + generate_solidity_files(guests.as_slice(), &solidity_opts).unwrap(); +} diff --git a/examples/governance/methods/guest/Cargo.lock b/examples/governance/methods/guest/Cargo.lock new file mode 100644 index 00000000..fd81f0d6 --- /dev/null +++ b/examples/governance/methods/guest/Cargo.lock @@ -0,0 +1,1259 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "anyhow" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-crypto-primitives" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3a13b34da09176a8baba701233fdffbaa7c1b1192ce031a3da4e55ce1f1a56" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-relations", + "ark-serialize", + "ark-snark", + "ark-std", + "blake2", + "derivative", + "digest", + "sha2", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-groth16" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20ceafa83848c3e390f1cbf124bc3193b3e639b3f02009e0e290809a501b95fc" +dependencies = [ + "ark-crypto-primitives", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-relations", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-relations" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00796b6efc05a3f48225e59cb6a2cda78881e7c390872d5786aaf112f31fb4f0" +dependencies = [ + "ark-ff", + "ark-std", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-snark" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d3cc6833a335bb8a600241889ead68ee89a3cf8448081fb7694c0fe503da63" +dependencies = [ + "ark-ff", + "ark-relations", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.77", + "syn_derive", +] + +[[package]] +name = "bytemuck" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "git+https://github.com/risc0/RustCrypto-crypto-bigint?tag=v0.5.5-risczero.0#3ab63a6f1048833f7047d5a50532e4a4cc789384" +dependencies = [ + "generic-array", + "getrandom", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "serdect", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "governance-guest" +version = "0.1.0" +dependencies = [ + "k256", + "risc0-zkvm", + "tiny-keccak", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "k256" +version = "0.13.3" +source = "git+https://github.com/risc0/RustCrypto-elliptic-curves?tag=k256/v0.13.3-risczero.0#d4f457a04410397cbb652a67c168b6cd6e9757c4" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "serdect", + "sha2", +] + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "metal" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +dependencies = [ + "bitflags 2.6.0", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", + "paste", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "risc0-binfmt" +version = "1.2.0-alpha.1" +source = "git+https://github.com/risc0/risc0?branch=main#c789579dedca148bd7942e97e63bb96d1182a164" +dependencies = [ + "anyhow", + "borsh", + "elf", + "risc0-zkp", + "risc0-zkvm-platform", + "serde", + "tracing", +] + +[[package]] +name = "risc0-circuit-recursion" +version = "1.2.0-alpha.1" +source = "git+https://github.com/risc0/risc0?branch=main#c789579dedca148bd7942e97e63bb96d1182a164" +dependencies = [ + "anyhow", + "bytemuck", + "hex", + "metal", + "risc0-core", + "risc0-zkp", + "tracing", +] + +[[package]] +name = "risc0-circuit-rv32im" +version = "1.2.0-alpha.1" +source = "git+https://github.com/risc0/risc0?branch=main#c789579dedca148bd7942e97e63bb96d1182a164" +dependencies = [ + "anyhow", + "metal", + "risc0-binfmt", + "risc0-core", + "risc0-zkp", + "risc0-zkvm-platform", + "serde", + "tracing", +] + +[[package]] +name = "risc0-core" +version = "1.2.0-alpha.1" +source = "git+https://github.com/risc0/risc0?branch=main#c789579dedca148bd7942e97e63bb96d1182a164" +dependencies = [ + "bytemuck", + "rand_core", +] + +[[package]] +name = "risc0-groth16" +version = "1.2.0-alpha.1" +source = "git+https://github.com/risc0/risc0?branch=main#c789579dedca148bd7942e97e63bb96d1182a164" +dependencies = [ + "anyhow", + "ark-bn254", + "ark-ec", + "ark-groth16", + "ark-serialize", + "bytemuck", + "hex", + "num-bigint", + "num-traits", + "risc0-binfmt", + "risc0-zkp", + "serde", +] + +[[package]] +name = "risc0-zkp" +version = "1.2.0-alpha.1" +source = "git+https://github.com/risc0/risc0?branch=main#c789579dedca148bd7942e97e63bb96d1182a164" +dependencies = [ + "anyhow", + "blake2", + "borsh", + "bytemuck", + "cfg-if", + "digest", + "hex", + "hex-literal", + "metal", + "paste", + "rand_core", + "risc0-core", + "risc0-zkvm-platform", + "serde", + "sha2", + "tracing", +] + +[[package]] +name = "risc0-zkvm" +version = "1.2.0-alpha.1" +source = "git+https://github.com/risc0/risc0?branch=main#c789579dedca148bd7942e97e63bb96d1182a164" +dependencies = [ + "anyhow", + "borsh", + "bytemuck", + "getrandom", + "hex", + "risc0-binfmt", + "risc0-circuit-recursion", + "risc0-circuit-rv32im", + "risc0-core", + "risc0-groth16", + "risc0-zkp", + "risc0-zkvm-platform", + "rrs-lib", + "semver", + "serde", + "sha2", + "stability", + "tracing", +] + +[[package]] +name = "risc0-zkvm-platform" +version = "1.2.0-alpha.1" +source = "git+https://github.com/risc0/risc0?branch=main#c789579dedca148bd7942e97e63bb96d1182a164" +dependencies = [ + "bytemuck", + "getrandom", + "libm", + "stability", +] + +[[package]] +name = "rrs-lib" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4382d3af3a4ebdae7f64ba6edd9114fff92c89808004c4943b393377a25d001" +dependencies = [ + "downcast-rs", + "paste", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "git+https://github.com/risc0/RustCrypto-hashes?tag=sha2-v0.10.8-risczero.0#244dc3b08788f7a4ccce14c66896ae3b4f24c166" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stability" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" +dependencies = [ + "quote", + "syn 2.0.77", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "tracing-core", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] diff --git a/examples/governance/methods/guest/Cargo.toml b/examples/governance/methods/guest/Cargo.toml new file mode 100644 index 00000000..118f8039 --- /dev/null +++ b/examples/governance/methods/guest/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "governance-guest" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "finalize_votes" +path = "src/bin/finalize_votes.rs" + +[workspace] + +[dependencies] +k256 = { version = "=0.13.3", features = [ + "arithmetic", + "serde", + "expose-field", + "std", + "ecdsa", +], default-features = false } +risc0-zkvm = { git = "https://github.com/risc0/risc0", branch = "main", default-features = false, features = ["std"] } +tiny-keccak = { version = "2.0", features = ["keccak"] } + +[patch.crates-io] +# Placing these patch statement in the workspace Cargo.toml will add RISC Zero SHA-256 and bigint +# multiplication accelerator support for all downstream usages of the following crates. +sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" } +k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.3-risczero.0" } +crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" } + +[profile.release] +# Empirically observed to result in the best performance for this binary (in particular, better than +# "fat"). It's often a good idea to experiment with different optimization levels and options. +codegen-units = 1 +lto = "thin" +debug = true diff --git a/examples/governance/methods/guest/src/bin/finalize_votes.rs b/examples/governance/methods/guest/src/bin/finalize_votes.rs new file mode 100644 index 00000000..fa618827 --- /dev/null +++ b/examples/governance/methods/guest/src/bin/finalize_votes.rs @@ -0,0 +1,155 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{collections::BTreeMap, io::Read}; + +use k256::{ + ecdsa::{RecoveryId, Signature, VerifyingKey}, + elliptic_curve::sec1::ToEncodedPoint, + PublicKey, +}; +use risc0_zkvm::{ + guest::env, + sha::rust_crypto::{Digest as _, Sha256}, +}; +use tiny_keccak::{Hasher, Keccak}; + +const ENCODED_INPUT_CHUNK_SIZE: usize = 100; + +fn keccak256(bytes: &[u8]) -> [u8; 32] { + let mut digest = [0u8; 32]; + let mut hasher = Keccak::v256(); + hasher.update(bytes); + hasher.finalize(&mut digest); + digest +} + +/// Assert that the given bytes are zeroes. +/// `offset` is used only for inclusion in an error message on panic. +fn assert_zero(bytes: &[u8], offset: usize) { + for (i, byte) in bytes.iter().copied().enumerate() { + assert_eq!( + byte, + 0u8, + "expected byte to be zero at offset {}", + offset + i + ); + } +} + +/// Converts an Ethereum-convention recovery ID to the k256 RecoveryId type. +fn into_recovery_id(v: u8) -> Option { + match v { + 0 => Some(0), + 1 => Some(1), + 27 => Some(0), + 28 => Some(1), + v if v >= 35 => Some((v - 1) % 2), + _ => None, + } + .and_then(RecoveryId::from_byte) +} + +/// Signer address recovery from the (v, r, s) signature components. +/// +/// This methods exists to replicate the behavior of `ecrecover` within the EVM. +/// It can only be considered a signature validation is digest is verified to be +/// the hash of a known message. +fn ecrecover(v: u8, rs: [u8; 64], digest: [u8; 32]) -> [u8; 20] { + let recovery_id = into_recovery_id(v).expect("value for v is invalid"); + let signature = Signature::from_slice(&rs[..]).expect("signature encoding is invalid"); + let recovered_pk: PublicKey = + VerifyingKey::recover_from_prehash(&digest[..], &signature, recovery_id) + .expect("signature is invalid") + .into(); + + // Calculate the Ethereum address from the k256 public key. + let encoded_pk = recovered_pk.to_encoded_point(/* compress = */ false); + keccak256(&encoded_pk.as_bytes()[1..])[12..] + .try_into() + .unwrap() +} + +fn main() { + // Read data sent from the application contract. + let mut input_bytes = Vec::::new(); + env::stdin().read_to_end(&mut input_bytes).unwrap(); + + // Input starts with a 32-byte proposal ID is then a packed encoding of 100-byte + // chunks of the form: + // When signed == true: + // u16(support) u8(signed) u8(v) [u8; 32](r) [u8; 32](s) [u8; 32](digest) + // When signed == false: + // u16(support) u8(signed) u8(v) [u8; 20](voter) [u8; 76](0) + let proposal_id: [u8; 32] = input_bytes[..32].try_into().unwrap(); + let chunks = input_bytes[32..].chunks_exact(ENCODED_INPUT_CHUNK_SIZE); + assert_eq!( + chunks.remainder().len(), + 0, + "input bytes must be a multiple of 100 bytes: {}", + input_bytes.len() + ); + + // Iterate through the 100-byte chunks, decoding each and reconstructing the + // ballot box hash accumulator value, and adding the votes to a map of votes. + let mut ballot_box_accum = proposal_id; + let mut hasher = Sha256::new(); + let mut votes = BTreeMap::<[u8; 20], u8>::new(); + for (offset, chunk) in chunks + .enumerate() + .map(|(i, chunk)| (i * ENCODED_INPUT_CHUNK_SIZE, chunk)) + { + assert_zero(&chunk[0..1], offset); + let signed: u8 = chunk[1]; + let support: u8 = chunk[2]; + let voter: [u8; 20] = match signed { + 0 => { + assert_zero(&chunk[3..4], offset); + assert_zero(&chunk[24..], offset + 24); + + hasher.update(&ballot_box_accum[..]); + hasher.update(&chunk[..24]); + hasher.finalize_into_reset((&mut ballot_box_accum).into()); + + chunk[4..24].try_into().unwrap() + } + 1 => { + let v: u8 = chunk[3]; + let rs: [u8; 64] = chunk[4..68].try_into().unwrap(); + let sig_digest: [u8; 32] = chunk[68..100].try_into().unwrap(); + + hasher.update(&ballot_box_accum[..]); + hasher.update(chunk); + hasher.finalize_into_reset((&mut ballot_box_accum).into()); + + ecrecover(v, rs, sig_digest) + } + _ => panic!("value of signed is invalid: {}", signed), + }; + // Add the vote to the map of votes, possibly replacing an existing vote. + votes.insert(voter, support); + } + + // Commit the proposal ID, ballot box hash, and 24-byte encoded ballots to the + // journal. + // NOTE: Padding bytes are inserted to maintain u32 word alignment. It is not + // well-established that this actually results in a more efficient program, + // so this needs to be tested. + env::commit_slice(&proposal_id[..]); + env::commit_slice(&ballot_box_accum[..]); + for (voter, support) in votes.iter() { + env::commit_slice(&u32::from(*support).to_be_bytes()[..]); + env::commit_slice(&voter[..]); + } +} diff --git a/examples/governance/methods/src/lib.rs b/examples/governance/methods/src/lib.rs new file mode 100644 index 00000000..bc3e1b11 --- /dev/null +++ b/examples/governance/methods/src/lib.rs @@ -0,0 +1,50 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Generated crate containing the image ID and ELF binary of the build guest. +include!(concat!(env!("OUT_DIR"), "/methods.rs")); + +#[cfg(test)] +mod test { + use alloy_primitives::hex_literal::hex; + use risc0_zkvm::{default_executor, ExecutorEnv}; + + use crate::FINALIZE_VOTES_ELF; + + const TEST_INPUT: &'static [u8] = &hex!( + "123ef2afce66c417062d3d2c69ca0a612c95de6ae9331e5e9640a361b787c1c8" + "000001004f81992fce2e1846dd528ec0102e6ee1f61ed3e20000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0001001bf6aa43f8d7be0bb024a5c78f3306de98255be17d70a6c6a55af54bb15a21301d41b6777aec47f9cf191533f0c351" + "eca97fde3756db8dd50882a26dcfa5ea0465615a2bd0468e6a715cf9378e9b28ba4314d567dd731e083c4b3d6f44e8f03bfb" + ); + + const TEST_OUTPUT: &'static [u8] = &hex!( + "123ef2afce66c417062d3d2c69ca0a612c95de6ae9331e5e9640a361b787c1c8" + "2b45288717bd1179cdda9be4ae9cb416e4e42028537046902c3a173596b4d623" + "000000014f81992fce2e1846dd528ec0102e6ee1f61ed3e2" + "00000000fbf974a059aa2e376f258d6c3238ad6c2bdb58ca" + ); + + #[test] + fn process_basic_finalization_input() { + let env = ExecutorEnv::builder() + .write_slice(&TEST_INPUT) + .build() + .unwrap(); + let exec = default_executor(); + let session = exec.execute(env, FINALIZE_VOTES_ELF).unwrap(); + assert_eq!(&session.journal.bytes, TEST_OUTPUT); + } +} diff --git a/examples/governance/remappings.txt b/examples/governance/remappings.txt new file mode 100644 index 00000000..515a0e6f --- /dev/null +++ b/examples/governance/remappings.txt @@ -0,0 +1,3 @@ +forge-std/=../../lib/forge-std/src/ +openzeppelin/=../../lib/openzeppelin-contracts/ +risc0/=../../contracts/src/ diff --git a/examples/governance/rust-toolchain.toml b/examples/governance/rust-toolchain.toml new file mode 100644 index 00000000..6c18dfcd --- /dev/null +++ b/examples/governance/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "stable" +components = ["rustfmt", "rust-src"] +profile = "minimal" \ No newline at end of file