From 169400e9c4163a52d2d9f5931c3b691ee76fdec2 Mon Sep 17 00:00:00 2001 From: srdtrk <59252793+srdtrk@users.noreply.github.com> Date: Fri, 29 Nov 2024 18:58:35 +0800 Subject: [PATCH] feat: move `sp1-ics07-tendermint` repo here (#128) --- .env.example | 21 +- .github/workflows/e2e.yml | 45 +- .github/workflows/rust.yml | 26 + .gitignore | 4 +- Cargo.lock | 707 ++++++++++++++---- Cargo.toml | 68 +- README.md | 67 +- buf.gen.yaml | 2 +- buf.yaml | 2 +- bun.lockb | Bin 77741 -> 76700 bytes .../light-clients/ISP1ICS07Tendermint.sol | 43 ++ .../light-clients/SP1ICS07Tendermint.sol | 536 +++++++++++++ .../errors/ISP1ICS07TendermintErrors.sol | 119 +++ .../msgs/IICS07TendermintMsgs.sol | 48 ++ .../light-clients/msgs/IMembershipMsgs.sol | 56 ++ .../light-clients/msgs/IMisbehaviourMsgs.sol | 31 + contracts/light-clients/msgs/ISP1Msgs.sol | 23 + .../msgs/IUcAndMembershipMsgs.sol | 18 + .../light-clients/msgs/IUpdateClientMsgs.sol | 31 + contracts/light-clients/utils/Paths.sol | 23 + e2e/interchaintestv8/cosmos/utils.go | 23 + e2e/interchaintestv8/e2esuite/utils.go | 126 ++++ e2e/interchaintestv8/ethereum/ethereum.go | 11 +- e2e/interchaintestv8/ethereum/utils.go | 13 + e2e/interchaintestv8/ibc_eureka_test.go | 10 +- e2e/interchaintestv8/operator/operator.go | 314 +++++++- e2e/interchaintestv8/operator/proofs.go | 15 +- e2e/interchaintestv8/relayer/relayer.go | 8 +- e2e/interchaintestv8/sp1_ics07_test.go | 699 +++++++++++++++++ e2e/interchaintestv8/testvalues/values.go | 16 +- e2e/interchaintestv8/types/fixtures.go | 2 +- foundry.toml | 1 + justfile | 69 +- package.json | 3 +- packages/prover/Cargo.toml | 24 + packages/prover/README.md | 3 + packages/prover/build.rs | 38 + packages/prover/src/lib.rs | 5 + packages/prover/src/programs.rs | 49 ++ packages/prover/src/prover.rs | 240 ++++++ packages/relayer-lib/Cargo.toml | 17 +- .../relayer-lib/src/tx_builder/eth_eureka.rs | 12 +- packages/solidity/Cargo.toml | 13 +- packages/solidity/src/lib.rs | 1 + packages/solidity/src/sp1_ics07.rs | 138 ++++ packages/utils/Cargo.toml | 22 + packages/utils/README.md | 3 + packages/utils/src/eth.rs | 21 + packages/utils/src/lib.rs | 7 + packages/utils/src/light_block.rs | 94 +++ packages/utils/src/merkle.rs | 22 + packages/utils/src/rpc.rs | 118 +++ programs/operator/Cargo.toml | 42 ++ programs/operator/src/bin/operator.rs | 34 + programs/operator/src/cli/command.rs | 257 +++++++ programs/operator/src/cli/mod.rs | 3 + programs/operator/src/lib.rs | 5 + .../src/runners/fixtures/membership.rs | 161 ++++ .../src/runners/fixtures/misbehaviour.rs | 141 ++++ programs/operator/src/runners/fixtures/mod.rs | 6 + .../src/runners/fixtures/uc_and_mem.rs | 132 ++++ .../src/runners/fixtures/update_client.rs | 111 +++ programs/operator/src/runners/genesis.rs | 125 ++++ programs/operator/src/runners/mod.rs | 5 + programs/operator/src/runners/operator.rs | 113 +++ {relayer => programs/relayer}/Cargo.toml | 0 {relayer => programs/relayer}/README.md | 0 {relayer => programs/relayer}/build.rs | 0 .../relayer}/config.example.json | 0 .../relayer}/proto/relayer/relayer.proto | 0 .../relayer}/src/bin/relayer.rs | 0 {relayer => programs/relayer}/src/cli/cmd.rs | 0 .../relayer}/src/cli/config.rs | 0 {relayer => programs/relayer}/src/cli/mod.rs | 0 .../relayer}/src/core/builder.rs | 0 {relayer => programs/relayer}/src/core/mod.rs | 0 .../relayer}/src/core/modules.rs | 0 {relayer => programs/relayer}/src/lib.rs | 0 .../relayer}/src/modules/cosmos_to_eth.rs | 0 .../relayer}/src/modules/mod.rs | 0 programs/sp1-programs/membership/Cargo.toml | 19 + programs/sp1-programs/membership/src/lib.rs | 63 ++ programs/sp1-programs/membership/src/main.rs | 49 ++ programs/sp1-programs/misbehaviour/Cargo.toml | 25 + programs/sp1-programs/misbehaviour/src/lib.rs | 91 +++ .../sp1-programs/misbehaviour/src/main.rs | 54 ++ .../misbehaviour/src/types/mod.rs | 3 + .../misbehaviour/src/types/validation.rs | 100 +++ .../sp1-programs/uc-and-membership/Cargo.toml | 23 + .../sp1-programs/uc-and-membership/src/lib.rs | 43 ++ .../uc-and-membership/src/main.rs | 73 ++ .../sp1-programs/update-client/Cargo.toml | 25 + .../sp1-programs/update-client/src/lib.rs | 61 ++ .../sp1-programs/update-client/src/main.rs | 46 ++ .../update-client/src/types/mod.rs | 3 + .../update-client/src/types/validation.rs | 95 +++ remappings.txt | 3 - scripts/Base.s.sol | 41 - scripts/Deploy.s.sol | 10 - scripts/E2ETestDeploy.s.sol | 14 +- scripts/SP1ICS07Tendermint.s.sol | 78 ++ test/{ => solidity-ibc}/BenchmarkTest.t.sol | 8 +- test/{ => solidity-ibc}/FixtureTest.t.sol | 20 +- test/{ => solidity-ibc}/IBCERC20Test.t.sol | 8 +- test/{ => solidity-ibc}/ICS02ClientTest.t.sol | 10 +- test/{ => solidity-ibc}/ICS20LibTest.t.sol | 2 +- .../ICS20TransferTest.t.sol | 18 +- test/{ => solidity-ibc}/ICS24HostTest.t.sol | 6 +- test/{ => solidity-ibc}/ICS26RouterTest.t.sol | 14 +- .../IbcIdentifiersTest.t.sol | 2 +- test/{ => solidity-ibc}/IntegrationTest.t.sol | 26 +- .../acknowledgeMultiPacket_1-groth16.json | 0 .../acknowledgeMultiPacket_1-plonk.json | 0 .../acknowledgeMultiPacket_25-groth16.json | 0 .../acknowledgeMultiPacket_50-groth16.json | 0 .../acknowledgeMultiPacket_50-plonk.json | 0 .../receiveMultiPacket_1-groth16.json | 0 .../fixtures/receiveMultiPacket_1-plonk.json | 0 .../receiveMultiPacket_25-groth16.json | 0 .../receiveMultiPacket_50-groth16.json | 0 .../fixtures/receiveMultiPacket_50-plonk.json | 0 .../fixtures/receiveNativePacket-groth16.json | 0 .../fixtures/receiveNativePacket-plonk.json | 0 .../fixtures/timeoutPacket-groth16.json | 0 .../fixtures/timeoutPacket-plonk.json | 0 .../mocks/DummyLightClient.sol | 2 +- .../mocks/ErroneousIBCStore.sol | 4 +- test/{ => solidity-ibc}/mocks/TestERC20.sol | 0 test/sp1-ics07/LargeMembership.t.sol | 64 ++ test/sp1-ics07/Membership.t.sol | 291 +++++++ test/sp1-ics07/MembershipTest.sol | 46 ++ test/sp1-ics07/Misbehaviour.t.sol | 210 ++++++ test/sp1-ics07/SP1ICS07TendermintTest.sol | 113 +++ test/sp1-ics07/UcAndMembership.t.sol | 219 ++++++ test/sp1-ics07/UpdateClient.t.sol | 150 ++++ .../membership_100-groth16_fixture.json | 10 + .../fixtures/membership_25-plonk_fixture.json | 10 + .../fixtures/memberships_fixture-groth16.json | 10 + .../fixtures/memberships_fixture-plonk.json | 10 + ...ing_time_monotonicity-groth16_fixture.json | 9 + ...isbehaviour_double_sign-plonk_fixture.json | 9 + .../uc_and_memberships_fixture-groth16.json | 10 + .../uc_and_memberships_fixture-plonk.json | 10 + .../update_client_fixture-groth16.json | 11 + .../fixtures/update_client_fixture-plonk.json | 11 + 145 files changed, 6952 insertions(+), 422 deletions(-) create mode 100644 contracts/light-clients/ISP1ICS07Tendermint.sol create mode 100644 contracts/light-clients/SP1ICS07Tendermint.sol create mode 100644 contracts/light-clients/errors/ISP1ICS07TendermintErrors.sol create mode 100644 contracts/light-clients/msgs/IICS07TendermintMsgs.sol create mode 100644 contracts/light-clients/msgs/IMembershipMsgs.sol create mode 100644 contracts/light-clients/msgs/IMisbehaviourMsgs.sol create mode 100644 contracts/light-clients/msgs/ISP1Msgs.sol create mode 100644 contracts/light-clients/msgs/IUcAndMembershipMsgs.sol create mode 100644 contracts/light-clients/msgs/IUpdateClientMsgs.sol create mode 100644 contracts/light-clients/utils/Paths.sol create mode 100644 e2e/interchaintestv8/cosmos/utils.go create mode 100644 e2e/interchaintestv8/sp1_ics07_test.go create mode 100644 packages/prover/Cargo.toml create mode 100644 packages/prover/README.md create mode 100644 packages/prover/build.rs create mode 100644 packages/prover/src/lib.rs create mode 100644 packages/prover/src/programs.rs create mode 100644 packages/prover/src/prover.rs create mode 100644 packages/solidity/src/sp1_ics07.rs create mode 100644 packages/utils/Cargo.toml create mode 100644 packages/utils/README.md create mode 100644 packages/utils/src/eth.rs create mode 100644 packages/utils/src/lib.rs create mode 100644 packages/utils/src/light_block.rs create mode 100644 packages/utils/src/merkle.rs create mode 100644 packages/utils/src/rpc.rs create mode 100644 programs/operator/Cargo.toml create mode 100644 programs/operator/src/bin/operator.rs create mode 100644 programs/operator/src/cli/command.rs create mode 100644 programs/operator/src/cli/mod.rs create mode 100644 programs/operator/src/lib.rs create mode 100644 programs/operator/src/runners/fixtures/membership.rs create mode 100644 programs/operator/src/runners/fixtures/misbehaviour.rs create mode 100644 programs/operator/src/runners/fixtures/mod.rs create mode 100644 programs/operator/src/runners/fixtures/uc_and_mem.rs create mode 100644 programs/operator/src/runners/fixtures/update_client.rs create mode 100644 programs/operator/src/runners/genesis.rs create mode 100644 programs/operator/src/runners/mod.rs create mode 100644 programs/operator/src/runners/operator.rs rename {relayer => programs/relayer}/Cargo.toml (100%) rename {relayer => programs/relayer}/README.md (100%) rename {relayer => programs/relayer}/build.rs (100%) rename {relayer => programs/relayer}/config.example.json (100%) rename {relayer => programs/relayer}/proto/relayer/relayer.proto (100%) rename {relayer => programs/relayer}/src/bin/relayer.rs (100%) rename {relayer => programs/relayer}/src/cli/cmd.rs (100%) rename {relayer => programs/relayer}/src/cli/config.rs (100%) rename {relayer => programs/relayer}/src/cli/mod.rs (100%) rename {relayer => programs/relayer}/src/core/builder.rs (100%) rename {relayer => programs/relayer}/src/core/mod.rs (100%) rename {relayer => programs/relayer}/src/core/modules.rs (100%) rename {relayer => programs/relayer}/src/lib.rs (100%) rename {relayer => programs/relayer}/src/modules/cosmos_to_eth.rs (100%) rename {relayer => programs/relayer}/src/modules/mod.rs (100%) create mode 100644 programs/sp1-programs/membership/Cargo.toml create mode 100644 programs/sp1-programs/membership/src/lib.rs create mode 100644 programs/sp1-programs/membership/src/main.rs create mode 100644 programs/sp1-programs/misbehaviour/Cargo.toml create mode 100644 programs/sp1-programs/misbehaviour/src/lib.rs create mode 100644 programs/sp1-programs/misbehaviour/src/main.rs create mode 100644 programs/sp1-programs/misbehaviour/src/types/mod.rs create mode 100644 programs/sp1-programs/misbehaviour/src/types/validation.rs create mode 100644 programs/sp1-programs/uc-and-membership/Cargo.toml create mode 100644 programs/sp1-programs/uc-and-membership/src/lib.rs create mode 100644 programs/sp1-programs/uc-and-membership/src/main.rs create mode 100644 programs/sp1-programs/update-client/Cargo.toml create mode 100644 programs/sp1-programs/update-client/src/lib.rs create mode 100644 programs/sp1-programs/update-client/src/main.rs create mode 100644 programs/sp1-programs/update-client/src/types/mod.rs create mode 100644 programs/sp1-programs/update-client/src/types/validation.rs delete mode 100644 scripts/Base.s.sol delete mode 100644 scripts/Deploy.s.sol create mode 100644 scripts/SP1ICS07Tendermint.s.sol rename test/{ => solidity-ibc}/BenchmarkTest.t.sol (95%) rename test/{ => solidity-ibc}/FixtureTest.t.sol (83%) rename test/{ => solidity-ibc}/IBCERC20Test.t.sol (94%) rename test/{ => solidity-ibc}/ICS02ClientTest.t.sol (81%) rename test/{ => solidity-ibc}/ICS20LibTest.t.sol (95%) rename test/{ => solidity-ibc}/ICS20TransferTest.t.sol (98%) rename test/{ => solidity-ibc}/ICS24HostTest.t.sol (96%) rename test/{ => solidity-ibc}/ICS26RouterTest.t.sol (85%) rename test/{ => solidity-ibc}/IbcIdentifiersTest.t.sol (97%) rename test/{ => solidity-ibc}/IntegrationTest.t.sol (98%) rename test/{ => solidity-ibc}/fixtures/acknowledgeMultiPacket_1-groth16.json (100%) rename test/{ => solidity-ibc}/fixtures/acknowledgeMultiPacket_1-plonk.json (100%) rename test/{ => solidity-ibc}/fixtures/acknowledgeMultiPacket_25-groth16.json (100%) rename test/{ => solidity-ibc}/fixtures/acknowledgeMultiPacket_50-groth16.json (100%) rename test/{ => solidity-ibc}/fixtures/acknowledgeMultiPacket_50-plonk.json (100%) rename test/{ => solidity-ibc}/fixtures/receiveMultiPacket_1-groth16.json (100%) rename test/{ => solidity-ibc}/fixtures/receiveMultiPacket_1-plonk.json (100%) rename test/{ => solidity-ibc}/fixtures/receiveMultiPacket_25-groth16.json (100%) rename test/{ => solidity-ibc}/fixtures/receiveMultiPacket_50-groth16.json (100%) rename test/{ => solidity-ibc}/fixtures/receiveMultiPacket_50-plonk.json (100%) rename test/{ => solidity-ibc}/fixtures/receiveNativePacket-groth16.json (100%) rename test/{ => solidity-ibc}/fixtures/receiveNativePacket-plonk.json (100%) rename test/{ => solidity-ibc}/fixtures/timeoutPacket-groth16.json (100%) rename test/{ => solidity-ibc}/fixtures/timeoutPacket-plonk.json (100%) rename test/{ => solidity-ibc}/mocks/DummyLightClient.sol (94%) rename test/{ => solidity-ibc}/mocks/ErroneousIBCStore.sol (88%) rename test/{ => solidity-ibc}/mocks/TestERC20.sol (100%) create mode 100644 test/sp1-ics07/LargeMembership.t.sol create mode 100644 test/sp1-ics07/Membership.t.sol create mode 100644 test/sp1-ics07/MembershipTest.sol create mode 100644 test/sp1-ics07/Misbehaviour.t.sol create mode 100644 test/sp1-ics07/SP1ICS07TendermintTest.sol create mode 100644 test/sp1-ics07/UcAndMembership.t.sol create mode 100644 test/sp1-ics07/UpdateClient.t.sol create mode 100644 test/sp1-ics07/fixtures/membership_100-groth16_fixture.json create mode 100644 test/sp1-ics07/fixtures/membership_25-plonk_fixture.json create mode 100644 test/sp1-ics07/fixtures/memberships_fixture-groth16.json create mode 100644 test/sp1-ics07/fixtures/memberships_fixture-plonk.json create mode 100644 test/sp1-ics07/fixtures/misbehaviour_breaking_time_monotonicity-groth16_fixture.json create mode 100644 test/sp1-ics07/fixtures/misbehaviour_double_sign-plonk_fixture.json create mode 100644 test/sp1-ics07/fixtures/uc_and_memberships_fixture-groth16.json create mode 100644 test/sp1-ics07/fixtures/uc_and_memberships_fixture-plonk.json create mode 100644 test/sp1-ics07/fixtures/update_client_fixture-groth16.json create mode 100644 test/sp1-ics07/fixtures/update_client_fixture-plonk.json diff --git a/.env.example b/.env.example index ac490d16..b3929dae 100644 --- a/.env.example +++ b/.env.example @@ -1,16 +1,17 @@ -# Mnemonic used in base script (not e2e) -export MNEMONIC="YOUR_MNEMONIC" +# Which type of ethereum testnet to run locally (pow|pos) +ETH_TESTNET_TYPE="pow" # SP1_PROVER={network|local|mock} SP1_PROVER=mock - # Private key with the permission to use the network prover (optional if you use SP1_PROVER=mock) SP1_PRIVATE_KEY="PRIVATE_KEY" +# Private key which the operator uses to sign the transactions in Eth Sepolia testnet +PRIVATE_KEY="PRIVATE-KEY" -# Which type of ethereum testnet to run locally (pow|pos) -ETH_TESTNET_TYPE="pow" - -# Optional field to specify the revision (in the form of a commit hash) of the SP1 operator -# If not specified the default revision from justfile will be used when building the operator with `just install-operator` -# Format: SP1_OPERATOR_REV={a commit hash of `https://github.com/cosmos/sp1-ics07-tendermint`} -# Example: SP1_OPERATOR_REV=f67f5fec9423a4744092ee98b62bc60e3354f223 +# URL of the Tendermint RPC node +TENDERMINT_RPC_URL=http://public-celestia-mocha4-consensus.numia.xyz/ +# URL of the Ethereum RPC node +# use https://ethereum-sepolia.publicnode.com/ for the Eth Sepolia testnet +RPC_URL=https://ethereum-holesky-rpc.publicnode.com +# Address of the light client contract +CONTRACT_ADDRESS="CONTRACT-ADDRESS" diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index f9beff5d..db2c3512 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -27,7 +27,6 @@ on: - 'bun.lockb' env: FOUNDRY_PROFILE: ci - SP1_OPERATOR_REV: f67f5fec9423a4744092ee98b62bc60e3354f223 permissions: contents: read @@ -67,6 +66,20 @@ jobs: - TestWithIbcEurekaTestSuite/TestICS20TransferTimeoutFromEthereumToCosmosChain_Groth16 - TestWithIbcEurekaTestSuite/TestICS20TransferTimeoutFromEthereumToCosmosChain_Plonk - TestWithRelayerTestSuite/TestRelayerInfo + - TestWithSP1ICS07TendermintTestSuite/TestDeploy_Groth16 + - TestWithSP1ICS07TendermintTestSuite/TestDeploy_Plonk + - TestWithSP1ICS07TendermintTestSuite/TestUpdateClient_Groth16 + - TestWithSP1ICS07TendermintTestSuite/TestUpdateClient_Plonk + - TestWithSP1ICS07TendermintTestSuite/TestMembership_Groth16 + - TestWithSP1ICS07TendermintTestSuite/TestMembership_Plonk + - TestWithSP1ICS07TendermintTestSuite/TestUpdateClientAndMembership_Groth16 + - TestWithSP1ICS07TendermintTestSuite/TestUpdateClientAndMembership_Plonk + - TestWithSP1ICS07TendermintTestSuite/TestDoubleSignMisbehaviour_Groth16 + - TestWithSP1ICS07TendermintTestSuite/TestDoubleSignMisbehaviour_Plonk + - TestWithSP1ICS07TendermintTestSuite/TestBreakingTimeMonotonicityMisbehaviour_Groth16 + - TestWithSP1ICS07TendermintTestSuite/TestBreakingTimeMonotonicityMisbehaviour_Plonk + - TestWithSP1ICS07TendermintTestSuite/Test100Membership_Groth16 + - TestWithSP1ICS07TendermintTestSuite/Test25Membership_Plonk name: ${{ matrix.test }} runs-on: ubuntu-latest env: @@ -85,47 +98,35 @@ jobs: check-latest: true cache-dependency-path: e2e/interchaintestv8/go.sum - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - components: rustfmt, clippy - - - name: "Cache Operator" - id: cache-operator - uses: actions/cache@v4 - with: - path: ~/.cargo/bin/operator - key: ${{ runner.os }}-operator-${{ env.SP1_OPERATOR_REV }} - - - name: "Cache Relayer" + - name: "Cache Relayer and Operator" id: cache-relayer uses: actions/cache@v4 with: - path: ~/.cargo/bin/relayer - key: ${{ runner.os }}-relayer-${{ hashFiles('Cargo.lock', 'packages/**', 'relayer/**') }} + path: | + ~/.cargo/bin/relayer + ~/.cargo/bin/operator + key: ${{ runner.os }}-relayer-${{ hashFiles('Cargo.lock', 'packages/**', 'programs/**') }} - name: Install SP1 toolchain - if: (steps.cache-operator.outputs.cache-hit != 'true') || (steps.cache-relayer.outputs.cache-hit != 'true') + if: (steps.cache-relayer.outputs.cache-hit != 'true') run: | curl -L https://sp1.succinct.xyz | bash ~/.sp1/bin/sp1up --token ${{ secrets.GITHUB_TOKEN }} ~/.sp1/bin/cargo-prove prove --version - name: Install operator - if: steps.cache-operator.outputs.cache-hit != 'true' + if: steps.cache-relayer.outputs.cache-hit != 'true' uses: actions-rs/cargo@v1 with: command: install - args: --git https://github.com/cosmos/sp1-ics07-tendermint --rev ${{ env.SP1_OPERATOR_REV }} sp1-ics07-tendermint-operator --bin operator --locked + args: --bin operator --path programs/operator --locked - name: Install relayer if: steps.cache-relayer.outputs.cache-hit != 'true' uses: actions-rs/cargo@v1 with: command: install - args: --bin relayer --path relayer --locked + args: --bin relayer --path programs/relayer --locked - name: Setup Kurtosis if: env.ETH_TESTNET_TYPE == 'pos' diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 08d13909..50b648fd 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -24,6 +24,11 @@ jobs: ~/.sp1/bin/sp1up --token ${{ secrets.GITHUB_TOKEN }} ~/.sp1/bin/cargo-prove prove --version + - name: Install just + uses: extractions/setup-just@v2 + - name: Build SP1 Programs + run: just build-sp1-programs + - name: Run cargo fmt uses: actions-rs/cargo@v1 with: @@ -75,3 +80,24 @@ jobs: with: command: build args: --bin relayer --release --locked + + build-operator: + name: build-operator + runs-on: ubuntu-latest + continue-on-error: true + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: "Set up environment" + uses: ./.github/setup + - name: Install SP1 toolchain + shell: bash + run: | + curl -L https://sp1.succinct.xyz | bash + ~/.sp1/bin/sp1up --token ${{ secrets.GITHUB_TOKEN }} + ~/.sp1/bin/cargo-prove prove --version + - name: Build + uses: actions-rs/cargo@v1 + with: + command: build + args: --bin operator --release --locked diff --git a/.gitignore b/.gitignore index 910de6da..fadd30ff 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ broadcast/*/31337/ # Rust target +elf # Config files and test artifacts -relayer/config.json +programs/relayer/config.json +scripts/genesis.json diff --git a/Cargo.lock b/Cargo.lock index e9d6732e..63d6faa0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,22 +79,20 @@ checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "alloy" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b524b8c28a7145d1fe4950f84360b5de3e307601679ff0558ddc20ea229399" +checksum = "98452d9acf0e74c318625cbd45342c95ba6302214391baf23d28c7ae480fa80b" dependencies = [ "alloy-consensus", "alloy-contract", "alloy-core", "alloy-eips", - "alloy-genesis", "alloy-network", "alloy-node-bindings", "alloy-provider", "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-serde", "alloy-signer", "alloy-signer-local", "alloy-transport", @@ -109,21 +107,22 @@ version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18c5c520273946ecf715c0010b4e3503d7eba9893cd9ce6b7fff5654c4a3c470" dependencies = [ - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "num_enum 0.7.3", "strum", ] [[package]] name = "alloy-consensus" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae09ffd7c29062431dd86061deefe4e3c6f07fa0d674930095f8dcedb0baf02c" +checksum = "3a1ff8439834ab71a4b0ecd1a8ff80b3921c87615f158940c3364f399c732786" dependencies = [ "alloy-eips", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-rlp", "alloy-serde", + "alloy-trie", "auto_impl", "c-kzg", "derive_more 1.0.0", @@ -131,21 +130,35 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-consensus-any" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519a86faaa6729464365a90c04eba68539b6d3a30f426edb4b3dafd78920d42f" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives 0.8.14", + "alloy-rlp", + "alloy-serde", + "serde", +] + [[package]] name = "alloy-contract" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66430a72d5bf5edead101c8c2f0a24bada5ec9f3cf9909b3e08b6d6899b4803e" +checksum = "cca2b353d8b7f160dc930dfa174557acefece6deab5ecd7e6230d38858579eea" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-network", "alloy-network-primitives", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-provider", "alloy-pubsub", "alloy-rpc-types-eth", - "alloy-sol-types 0.8.13", + "alloy-sol-types 0.8.14", "alloy-transport", "futures", "futures-util", @@ -154,27 +167,26 @@ dependencies = [ [[package]] name = "alloy-core" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d22df68fa7d9744be0b1a9be3260e9aa089fbf41903ab182328333061ed186" +checksum = "c3d14d531c99995de71558e8e2206c27d709559ee8e5a0452b965ea82405a013" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", - "alloy-primitives 0.8.13", - "alloy-rlp", - "alloy-sol-types 0.8.13", + "alloy-primitives 0.8.14", + "alloy-sol-types 0.8.14", ] [[package]] name = "alloy-dyn-abi" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cf633ae9a1f0c82fdb9e559ed2be1c8e415c3e48fc47e1feaf32c6078ec0cdd" +checksum = "80759b3f57b3b20fa7cd8fef6479930fc95461b58ff8adea6e87e618449c8a1d" dependencies = [ "alloy-json-abi", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-sol-type-parser", - "alloy-sol-types 0.8.13", + "alloy-sol-types 0.8.14", "const-hex", "itoa", "serde", @@ -188,7 +200,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" dependencies = [ - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-rlp", "serde", ] @@ -199,7 +211,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c986539255fb839d1533c128e190e557e52ff652c9ef62939e233a81dd93f7e" dependencies = [ - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-rlp", "derive_more 1.0.0", "k256", @@ -208,13 +220,13 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6aa3961694b30ba53d41006131a2fca3bdab22e4c344e46db2c639e7c2dfdd" +checksum = "8dedb328c2114284f767e075589ca9de8d5e9c8a91333402f4804a584ed71a38" dependencies = [ "alloy-eip2930", "alloy-eip7702", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-rlp", "alloy-serde", "c-kzg", @@ -226,22 +238,22 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53f7877ded3921d18a0a9556d55bedf84535567198c9edab2aa23106da91855" +checksum = "4841e8dd4e0f53d76b501fd4c6bc21d95d688bc8ebf0ea359fc6c7ab65b48742" dependencies = [ - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-serde", "serde", ] [[package]] name = "alloy-json-abi" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a500037938085feed8a20dbfc8fce58c599db68c948cfae711147175dee392c" +checksum = "ac4b22b3e51cac09fd2adfcc73b55f447b4df669f983c13f7894ec82b607c63f" dependencies = [ - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-sol-type-parser", "serde", "serde_json", @@ -249,12 +261,12 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3694b7e480728c0b3e228384f223937f14c10caef5a4c766021190fc8f283d35" +checksum = "254f770918f96dc4ec88a15e6e2e243358e1719d66b40ef814428e7697079d25" dependencies = [ - "alloy-primitives 0.8.13", - "alloy-sol-types 0.8.13", + "alloy-primitives 0.8.14", + "alloy-sol-types 0.8.14", "serde", "serde_json", "thiserror 1.0.69", @@ -263,19 +275,21 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea94b8ceb5c75d7df0a93ba0acc53b55a22b47b532b600a800a87ef04eb5b0b4" +checksum = "931dd176c6e33355f3dc0170ec69cf5b951f4d73870b276e2c837ab35f9c5136" dependencies = [ "alloy-consensus", + "alloy-consensus-any", "alloy-eips", "alloy-json-rpc", "alloy-network-primitives", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", + "alloy-rpc-types-any", "alloy-rpc-types-eth", "alloy-serde", "alloy-signer", - "alloy-sol-types 0.8.13", + "alloy-sol-types 0.8.14", "async-trait", "auto_impl", "futures-utils-wasm", @@ -286,25 +300,25 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9f3e281005943944d15ee8491534a1c7b3cbf7a7de26f8c433b842b93eb5f9" +checksum = "fa6ec0f23be233e851e31c5e4badfedfa9c7bc177bc37f4e03616072cd40a806" dependencies = [ "alloy-consensus", "alloy-eips", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-serde", "serde", ] [[package]] name = "alloy-node-bindings" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9805d126f24be459b958973c0569c73e1aadd27d4535eee82b2b6764aa03616" +checksum = "e3bce85f0f67b2248c2eb42941bb75079ac53648569a668e8bfd7de5a831ec64" dependencies = [ "alloy-genesis", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "k256", "rand", "serde_json", @@ -338,9 +352,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aeeb5825c2fc8c2662167058347cd0cafc3cb15bcb5cdb1758a63c2dca0409e" +checksum = "9db948902dfbae96a73c2fbf1f7abec62af034ab883e4c777c3fd29702bd6e2c" dependencies = [ "alloy-rlp", "bytes", @@ -366,9 +380,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c1f9eede27bf4c13c099e8e64d54efd7ce80ef6ea47478aa75d5d74e2dba3b" +checksum = "5545e2cbf2f8f24c68bb887ba0294fa12a2f816b9e72c4f226cd137b77d0e294" dependencies = [ "alloy-chains", "alloy-consensus", @@ -377,7 +391,7 @@ dependencies = [ "alloy-network", "alloy-network-primitives", "alloy-node-bindings", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types-anvil", @@ -410,12 +424,12 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f1f34232f77341076541c405482e4ae12f0ee7153d8f9969fc1691201b2247" +checksum = "b633f7731a3df2f4f334001bf80436565113816c5aa5c136c1ded563051e049b" dependencies = [ "alloy-json-rpc", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-transport", "bimap", "futures", @@ -451,12 +465,12 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374dbe0dc3abdc2c964f36b3d3edf9cdb3db29d16bda34aa123f03d810bec1dd" +checksum = "aed9e40c2a73265ebf70f1e48303ee55920282e1ea5971e832873fb2d32cea74" dependencies = [ "alloy-json-rpc", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-pubsub", "alloy-transport", "alloy-transport-http", @@ -477,11 +491,11 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c74832aa474b670309c20fffc2a869fa141edab7c79ff7963fad0a08de60bae1" +checksum = "42dea20fa715a6f39ec7adc735cfd9567342870737270ac67795d55896527772" dependencies = [ - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-rpc-types-engine", "alloy-rpc-types-eth", "alloy-serde", @@ -490,25 +504,37 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca97963132f78ddfc60e43a017348e6d52eea983925c23652f5b330e8e02291" +checksum = "2750f4f694b27461915b9794df60177198bf733da38dde71aadfbe2946a3c0be" dependencies = [ - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-rpc-types-eth", "alloy-serde", "serde", ] +[[package]] +name = "alloy-rpc-types-any" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79d7620e22d6ed7c58451dd303d0501ade5a8bec9dc8daef0fbc48ceffabbae1" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-rpc-types-eth", + "alloy-serde", +] + [[package]] name = "alloy-rpc-types-engine" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56294dce86af23ad6ee8df46cf8b0d292eb5d1ff67dc88a0886051e32b1faf" +checksum = "9fb843daa6feb011475f0db8c499fff5ac62e1e6012fc01d97477ddb3217a83f" dependencies = [ "alloy-consensus", "alloy-eips", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-rlp", "alloy-serde", "derive_more 1.0.0", @@ -518,17 +544,18 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a477281940d82d29315846c7216db45b15e90bcd52309da9f54bcf7ad94a11" +checksum = "df34b88df4deeac9ecfc80ad7cbb26a33e57437b9db8be5b952792feef6134bc" dependencies = [ "alloy-consensus", + "alloy-consensus-any", "alloy-eips", "alloy-network-primitives", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-rlp", "alloy-serde", - "alloy-sol-types 0.8.13", + "alloy-sol-types 0.8.14", "derive_more 1.0.0", "itertools 0.13.0", "serde", @@ -537,22 +564,22 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dfa4a7ccf15b2492bb68088692481fd6b2604ccbee1d0d6c44c21427ae4df83" +checksum = "43a89fd4cc3f96b3c5c0dd1cebeb63323e4659bbdc837117fa3fd5ac168df7d9" dependencies = [ - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "serde", "serde_json", ] [[package]] name = "alloy-signer" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e10aec39d60dc27edcac447302c7803d2371946fb737245320a05b78eb2fafd" +checksum = "532010243a96d1f8593c2246ec3971bc52303884fa1e43ca0a776798ba178910" dependencies = [ - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "async-trait", "auto_impl", "elliptic-curve", @@ -562,13 +589,13 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8396f6dff60700bc1d215ee03d86ff56de268af96e2bf833a14d0bafcab9882" +checksum = "e8080c0ab2dc729b0cbb183843d08e78d2a1629140c9fc16234d2272abb483bd" dependencies = [ "alloy-consensus", "alloy-network", - "alloy-primitives 0.8.13", + "alloy-primitives 0.8.14", "alloy-signer", "async-trait", "k256", @@ -592,12 +619,12 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c0279d09463a4695788a3622fd95443625f7be307422deba4b55dd491a9c7a1" +checksum = "3bfd7853b65a2b4f49629ec975fee274faf6dff15ab8894c620943398ef283c0" dependencies = [ - "alloy-sol-macro-expander 0.8.13", - "alloy-sol-macro-input 0.8.13", + "alloy-sol-macro-expander 0.8.14", + "alloy-sol-macro-input 0.8.14", "proc-macro-error2", "proc-macro2", "quote", @@ -624,12 +651,12 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4feea540fc8233df2ad1156efd744b2075372f43a8f942a68b3b19c8a00e2c12" +checksum = "82ec42f342d9a9261699f8078e57a7a4fda8aaa73c1a212ed3987080e6a9cd13" dependencies = [ "alloy-json-abi", - "alloy-sol-macro-input 0.8.13", + "alloy-sol-macro-input 0.8.14", "const-hex", "heck", "indexmap 2.6.0", @@ -637,7 +664,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.89", - "syn-solidity 0.8.13", + "syn-solidity 0.8.14", "tiny-keccak", ] @@ -658,9 +685,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0ad281f3d1b613af814b66977ee698e443d4644a1510962d0241f26e0e53ae" +checksum = "ed2c50e6a62ee2b4f7ab3c6d0366e5770a21cad426e109c2f40335a1b3aff3df" dependencies = [ "alloy-json-abi", "const-hex", @@ -670,14 +697,14 @@ dependencies = [ "quote", "serde_json", "syn 2.0.89", - "syn-solidity 0.8.13", + "syn-solidity 0.8.14", ] [[package]] name = "alloy-sol-type-parser" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96eff16c797438add6c37bb335839d015b186c5421ee5626f5559a7bfeb38ef5" +checksum = "ac17c6e89a50fb4a758012e4b409d9a0ba575228e69b539fe37d7a1bd507ca4a" dependencies = [ "serde", "winnow 0.6.20", @@ -697,22 +724,22 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff34e0682d6665da243a3e81da96f07a2dd50f7e64073e382b1a141f5a2a2f6" +checksum = "c9dc0fffe397aa17628160e16b89f704098bf3c9d74d5d369ebc239575936de5" dependencies = [ "alloy-json-abi", - "alloy-primitives 0.8.13", - "alloy-sol-macro 0.8.13", + "alloy-primitives 0.8.14", + "alloy-sol-macro 0.8.14", "const-hex", "serde", ] [[package]] name = "alloy-transport" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f99acddb34000d104961897dbb0240298e8b775a7efffb9fda2a1a3efedd65b3" +checksum = "b6f295f4b745fb9e4e663d70bc57aed991288912c7aaaf25767def921050ee43" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -730,9 +757,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc013132e34eeadaa0add7e74164c1503988bfba8bae885b32e0918ba85a8a6" +checksum = "39139015a5ec127d9c895b49b484608e27fe4538544f84cdf5eae0bd36339bc6" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -745,9 +772,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063edc0660e81260653cc6a95777c29d54c2543a668aa5da2359fb450d25a1ba" +checksum = "d9b4f865b13bb8648e93f812b19b74838b9165212a2beb95fc386188c443a5e3" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -764,15 +791,15 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd170e600801116d5efe64f74a4fc073dbbb35c807013a7d0a388742aeebba0" +checksum = "6af91e3521b8b3eac26809b1c6f9b86e3ed455dfab812f036836aabdf709b921" dependencies = [ "alloy-pubsub", "alloy-transport", "futures", "http 1.1.0", - "rustls 0.23.18", + "rustls 0.23.19", "serde_json", "tokio", "tokio-tungstenite", @@ -780,6 +807,21 @@ dependencies = [ "ws_stream_wasm", ] +[[package]] +name = "alloy-trie" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b2e366c0debf0af77766c23694a3f863b02633050e71e096e257ffbd395e50" +dependencies = [ + "alloy-primitives 0.8.14", + "alloy-rlp", + "arrayvec", + "derive_more 1.0.0", + "nybbles", + "smallvec", + "tracing", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -1355,9 +1397,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" dependencies = [ "serde", ] @@ -1388,9 +1430,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -1450,6 +1492,7 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", + "serde", "windows-targets 0.52.6", ] @@ -1587,9 +1630,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487981fa1af147182687064d0a2c336586d337a606595ced9ffb0c685c250c73" +checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" dependencies = [ "cfg-if", "cpufeatures", @@ -1718,14 +1761,15 @@ dependencies = [ [[package]] name = "curve25519-dalek" version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +source = "git+https://github.com/sp1-patches/curve25519-dalek?branch=patch-curve25519-v4.1.3#a9d46282f5660dfb7e3850ef957fe884089daeda" dependencies = [ + "anyhow", "cfg-if", "cpufeatures", "curve25519-dalek-derive", "fiat-crypto", "rustc_version 0.4.1", + "sp1-lib", "subtle", "zeroize", ] @@ -1733,8 +1777,7 @@ dependencies = [ [[package]] name = "curve25519-dalek-derive" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +source = "git+https://github.com/sp1-patches/curve25519-dalek?branch=patch-curve25519-v4.1.3#a9d46282f5660dfb7e3850ef957fe884089daeda" dependencies = [ "proc-macro2", "quote", @@ -1744,16 +1787,53 @@ dependencies = [ [[package]] name = "curve25519-dalek-ng" version = "4.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +source = "git+https://github.com/sp1-patches/curve25519-dalek-ng?branch=patch-v4.1.1#3fb3e7f6047ddeef0f0c9212f4604bd30d64bd28" dependencies = [ + "anyhow", "byteorder", + "cfg-if", "digest 0.9.0", "rand_core", + "sp1-lib", "subtle-ng", "zeroize", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.89", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.89", +] + [[package]] name = "dashmap" version = "6.1.0" @@ -1975,6 +2055,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "downcast-rs" version = "1.2.1" @@ -1990,14 +2076,17 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "ecdsa" version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +source = "git+https://github.com/sp1-patches/signatures?branch=patch-ecdsa-v0.16.9#475daa8834035cc170a567e7656329ab8de8cc44" dependencies = [ + "anyhow", + "cfg-if", "der", "digest 0.10.7", "elliptic-curve", + "hex-literal", "rfc6979", "signature", + "sp1-lib", "spki", ] @@ -2014,8 +2103,7 @@ dependencies = [ [[package]] name = "ed25519-consensus" version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +source = "git+https://github.com/sp1-patches/ed25519-consensus?branch=patch-v2.1.0#2b2c4b43344bc4daf5b1326f367f2d9d661eeabb" dependencies = [ "curve25519-dalek-ng", "hex", @@ -2117,12 +2205,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2486,7 +2574,6 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" dependencies = [ - "eyre", "paste", ] @@ -3040,7 +3127,7 @@ dependencies = [ "http 1.1.0", "hyper 1.5.1", "hyper-util", - "rustls 0.23.18", + "rustls 0.23.19", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -3132,6 +3219,24 @@ dependencies = [ "cc", ] +[[package]] +name = "ibc-client-tendermint" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5b02fb5248a9fdf1b8242792fbd5528f2d51aded7cb1a7a9c016b4e9df49610" +dependencies = [ + "derive_more 1.0.0", + "ibc-client-tendermint-types", + "ibc-core-client", + "ibc-core-commitment-types", + "ibc-core-handler-types", + "ibc-core-host", + "ibc-primitives", + "serde", + "tendermint", + "tendermint-light-client-verifier", +] + [[package]] name = "ibc-client-tendermint-types" version = "0.56.0" @@ -3150,6 +3255,57 @@ dependencies = [ "tendermint-proto", ] +[[package]] +name = "ibc-core-channel-types" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd8a7707fad74232cc5a306382a2c60a43346ea13bf6acc08b6b5289508b0c8" +dependencies = [ + "derive_more 1.0.0", + "displaydoc", + "ibc-core-client-types", + "ibc-core-commitment-types", + "ibc-core-connection-types", + "ibc-core-host-types", + "ibc-primitives", + "ibc-proto", + "serde", + "sha2 0.10.8", + "subtle-encoding", + "tendermint", +] + +[[package]] +name = "ibc-core-client" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5397f176e100ea3023734d9deb2ae1cc9e19b4561bf1aba2ae38a4d49272c6ad" +dependencies = [ + "ibc-core-client-context", + "ibc-core-client-types", + "ibc-core-commitment-types", + "ibc-core-handler-types", + "ibc-core-host", + "ibc-primitives", +] + +[[package]] +name = "ibc-core-client-context" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46639dacb4915502a5f22417ed2b5759919ea52b24906d139903d557555ca2c6" +dependencies = [ + "derive_more 1.0.0", + "displaydoc", + "ibc-core-client-types", + "ibc-core-commitment-types", + "ibc-core-handler-types", + "ibc-core-host-types", + "ibc-primitives", + "subtle-encoding", + "tendermint", +] + [[package]] name = "ibc-core-client-types" version = "0.56.0" @@ -3183,6 +3339,64 @@ dependencies = [ "subtle-encoding", ] +[[package]] +name = "ibc-core-connection-types" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f851d007b1c269eb849208e080a8790a2a5b37546b3e741d835316a739483492" +dependencies = [ + "derive_more 1.0.0", + "displaydoc", + "ibc-core-client-types", + "ibc-core-commitment-types", + "ibc-core-host-types", + "ibc-primitives", + "ibc-proto", + "serde", + "subtle-encoding", + "tendermint", +] + +[[package]] +name = "ibc-core-handler-types" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f69ac81e331c0740b0ee47ef9cbbf5f6f5c9cbbe4ca620d156224b653e63ac21" +dependencies = [ + "derive_more 1.0.0", + "displaydoc", + "ibc-core-channel-types", + "ibc-core-client-types", + "ibc-core-commitment-types", + "ibc-core-connection-types", + "ibc-core-host-types", + "ibc-core-router-types", + "ibc-primitives", + "ibc-proto", + "serde", + "subtle-encoding", + "tendermint", +] + +[[package]] +name = "ibc-core-host" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a48b932c262deb91616535a690f5c9e31182d32f1d8d0cf1caf01b1a7d370277" +dependencies = [ + "derive_more 1.0.0", + "displaydoc", + "ibc-core-channel-types", + "ibc-core-client-context", + "ibc-core-client-types", + "ibc-core-commitment-types", + "ibc-core-connection-types", + "ibc-core-handler-types", + "ibc-core-host-types", + "ibc-primitives", + "subtle-encoding", +] + [[package]] name = "ibc-core-host-types" version = "0.56.0" @@ -3197,6 +3411,22 @@ dependencies = [ "serde", ] +[[package]] +name = "ibc-core-router-types" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6e4fe4d5f146a06ba7fe9de72643edb92140297cbe639b457fe6a6863c9763c" +dependencies = [ + "derive_more 1.0.0", + "displaydoc", + "ibc-core-host-types", + "ibc-primitives", + "ibc-proto", + "serde", + "subtle-encoding", + "tendermint", +] + [[package]] name = "ibc-eureka-relayer-lib" version = "0.1.0" @@ -3210,7 +3440,6 @@ dependencies = [ "ibc-eureka-solidity-types", "serde", "sp1-ics07-tendermint-prover", - "sp1-ics07-tendermint-solidity", "sp1-ics07-tendermint-utils", "sp1-sdk", "tendermint", @@ -3222,9 +3451,15 @@ name = "ibc-eureka-solidity-types" version = "0.1.0" dependencies = [ "alloy-contract", - "alloy-sol-types 0.8.13", + "alloy-sol-types 0.8.14", "hex", + "ibc-client-tendermint-types", + "ibc-core-client-types", + "ibc-core-commitment-types", "serde", + "tendermint", + "tendermint-light-client-verifier", + "time", ] [[package]] @@ -3396,6 +3631,12 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.0.3" @@ -4055,6 +4296,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "nybbles" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95f06be0417d97f81fe4e5c86d7d01b392655a9cac9c19a848aa033e18937b23" +dependencies = [ + "const-hex", + "smallvec", +] + [[package]] name = "object" version = "0.36.5" @@ -4860,7 +5111,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.0.0", - "rustls 0.23.18", + "rustls 0.23.19", "socket2", "thiserror 2.0.3", "tokio", @@ -4878,7 +5129,7 @@ dependencies = [ "rand", "ring 0.17.8", "rustc-hash 2.0.0", - "rustls 0.23.18", + "rustls 0.23.19", "rustls-pki-types", "slab", "thiserror 2.0.3", @@ -5127,7 +5378,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.18", + "rustls 0.23.19", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", @@ -5167,8 +5418,7 @@ dependencies = [ [[package]] name = "rfc6979" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +source = "git+https://github.com/sp1-patches/signatures?branch=patch-ecdsa-v0.16.9#475daa8834035cc170a567e7656329ab8de8cc44" dependencies = [ "hmac", "subtle", @@ -5345,9 +5595,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.18" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "once_cell", "ring 0.17.8", @@ -5715,6 +5965,34 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "serial_test" version = "3.2.0" @@ -5754,8 +6032,7 @@ dependencies = [ [[package]] name = "sha2" version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +source = "git+https://github.com/sp1-patches/RustCrypto-hashes?branch=patch-v0.9.9#db82a4848f8d033eab544255e1efa036cc06f054" dependencies = [ "block-buffer 0.9.0", "cfg-if", @@ -5767,8 +6044,7 @@ dependencies = [ [[package]] name = "sha2" version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +source = "git+https://github.com/sp1-patches/RustCrypto-hashes?branch=patch-v0.10.8#1f224388fdede7cef649bce0d63876d1a9e3f515" dependencies = [ "cfg-if", "cpufeatures", @@ -5874,9 +6150,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -6040,42 +6316,120 @@ dependencies = [ ] [[package]] -name = "sp1-ics07-tendermint-prover" +name = "sp1-ics07-tendermint-membership" version = "0.1.0" -source = "git+https://github.com/cosmos/sp1-ics07-tendermint?rev=f67f5fec9423a4744092ee98b62bc60e3354f223#f67f5fec9423a4744092ee98b62bc60e3354f223" dependencies = [ + "alloy-sol-types 0.8.14", "bincode", - "ibc-client-tendermint-types", "ibc-core-commitment-types", + "ibc-eureka-solidity-types", "ibc-proto", - "log", + "sp1-zkvm", +] + +[[package]] +name = "sp1-ics07-tendermint-misbehaviour" +version = "0.1.0" +dependencies = [ + "alloy-sol-types 0.8.14", + "bincode", + "ibc-client-tendermint", + "ibc-core-client", + "ibc-core-handler-types", + "ibc-core-host-types", + "ibc-eureka-solidity-types", + "ibc-primitives", "serde_cbor", - "sp1-helper", - "sp1-ics07-tendermint-solidity", - "sp1-sdk", + "sha2 0.10.8", + "sp1-zkvm", + "tendermint-light-client-verifier", ] [[package]] -name = "sp1-ics07-tendermint-solidity" +name = "sp1-ics07-tendermint-operator" version = "0.1.0" -source = "git+https://github.com/cosmos/sp1-ics07-tendermint?rev=f67f5fec9423a4744092ee98b62bc60e3354f223#f67f5fec9423a4744092ee98b62bc60e3354f223" dependencies = [ - "alloy-contract", - "alloy-sol-types 0.8.13", + "alloy", + "alloy-primitives 0.8.14", + "alloy-sol-types 0.8.14", + "anyhow", + "clap", + "cosmos-sdk-proto", + "dotenv", + "futures", "hex", "ibc-client-tendermint-types", "ibc-core-client-types", "ibc-core-commitment-types", + "ibc-eureka-solidity-types", + "ibc-proto", + "reqwest 0.12.9", "serde", + "serde_json", + "serde_with", + "sp1-ics07-tendermint-prover", + "sp1-ics07-tendermint-utils", + "sp1-sdk", + "subtle-encoding", "tendermint", "tendermint-light-client-verifier", - "time", + "tendermint-rpc", + "tokio", + "tracing", +] + +[[package]] +name = "sp1-ics07-tendermint-prover" +version = "0.1.0" +dependencies = [ + "bincode", + "ibc-client-tendermint-types", + "ibc-core-commitment-types", + "ibc-eureka-solidity-types", + "ibc-proto", + "serde_cbor", + "sp1-helper", + "sp1-sdk", + "tracing", +] + +[[package]] +name = "sp1-ics07-tendermint-uc-and-membership" +version = "0.1.0" +dependencies = [ + "alloy-sol-types 0.8.14", + "bincode", + "ibc-client-tendermint-types", + "ibc-core-commitment-types", + "ibc-eureka-solidity-types", + "ibc-proto", + "serde_cbor", + "sp1-ics07-tendermint-membership", + "sp1-ics07-tendermint-update-client", + "sp1-zkvm", +] + +[[package]] +name = "sp1-ics07-tendermint-update-client" +version = "0.1.0" +dependencies = [ + "alloy-sol-types 0.8.14", + "bincode", + "ibc-client-tendermint", + "ibc-core-client", + "ibc-core-handler-types", + "ibc-core-host-types", + "ibc-eureka-solidity-types", + "ibc-primitives", + "serde_cbor", + "sha2 0.10.8", + "sp1-zkvm", + "tendermint-light-client-verifier", ] [[package]] name = "sp1-ics07-tendermint-utils" version = "0.1.0" -source = "git+https://github.com/cosmos/sp1-ics07-tendermint?rev=f67f5fec9423a4744092ee98b62bc60e3354f223#f67f5fec9423a4744092ee98b62bc60e3354f223" dependencies = [ "alloy", "anyhow", @@ -6085,14 +6439,24 @@ dependencies = [ "ibc-core-client-types", "ibc-core-commitment-types", "ibc-core-host-types", + "ibc-eureka-solidity-types", "prost", "serde", - "sp1-ics07-tendermint-solidity", "tendermint", "tendermint-light-client-verifier", "tendermint-rpc", ] +[[package]] +name = "sp1-lib" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa1fe3d3e2a4cfe6b7259f0c00f770a4376fba892af20695b6ba450e0d02771c" +dependencies = [ + "bincode", + "serde", +] + [[package]] name = "sp1-primitives" version = "3.3.0" @@ -6358,6 +6722,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "sp1-zkvm" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7123d0e616c6641a9088c604f5d00f728f396823e8a91ef805d54d6e1d8ccc1b" +dependencies = [ + "cfg-if", + "getrandom", + "lazy_static", + "libm", + "rand", + "sha2 0.10.8", + "sp1-lib", +] + [[package]] name = "spin" version = "0.5.2" @@ -6477,9 +6856,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdaa7b9e815582ba343a20c66627437cf45f1c6fba7f69772cbfd1358c7e197" +checksum = "da0523f59468a2696391f2a772edc089342aacd53c3caa2ac3264e598edf119b" dependencies = [ "paste", "proc-macro2", @@ -6787,9 +7166,9 @@ dependencies = [ [[package]] name = "tiny-keccak" version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +source = "git+https://github.com/sp1-patches/tiny-keccak?branch=patch-v2.0.2#bf0b28f63510a90c7b6c21ac6ff461c93ecd2331" dependencies = [ + "cfg-if", "crunchy", ] @@ -6873,7 +7252,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.18", + "rustls 0.23.19", "rustls-pki-types", "tokio", ] @@ -6898,7 +7277,7 @@ checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", - "rustls 0.23.18", + "rustls 0.23.19", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -7058,9 +7437,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -7160,7 +7539,7 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.23.18", + "rustls 0.23.19", "rustls-pki-types", "sha1", "thiserror 1.0.69", diff --git a/Cargo.toml b/Cargo.toml index 70ad7cfb..ee0ba0ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,9 @@ members = [ "packages/*", - "relayer/", + "programs/relayer", + "programs/operator", + "programs/sp1-programs/*", ] resolver = "2" @@ -14,22 +16,29 @@ repository = "https://github.com/cosmos/solidity-ibc-eureka" keywords = ["cosmos", "ibc", "sp1", "tendermint", "ethereum", "bridge", "solidity", "eureka"] [workspace.dependencies] -ibc-eureka-solidity-types = { path = "packages/solidity", default-features = false } -ibc-eureka-relayer-lib = { path = "packages/relayer-lib", default-features = false } +ibc-eureka-solidity-types = { path = "packages/solidity", default-features = false } +ibc-eureka-relayer-lib = { path = "packages/relayer-lib", default-features = false } +sp1-ics07-tendermint-prover = { path = "packages/prover", default-features = false } +sp1-ics07-tendermint-utils = { path = "packages/utils", default-features = false } +sp1-ics07-tendermint-update-client = { path = "programs/sp1-programs/update-client", default-features = false } +sp1-ics07-tendermint-membership = { path = "programs/sp1-programs/membership", default-features = false } -sp1-ics07-tendermint-solidity = { git = "https://github.com/cosmos/sp1-ics07-tendermint", rev = "f67f5fec9423a4744092ee98b62bc60e3354f223", default-features = false } -sp1-ics07-tendermint-prover = { git = "https://github.com/cosmos/sp1-ics07-tendermint", rev = "f67f5fec9423a4744092ee98b62bc60e3354f223", default-features = false } -sp1-ics07-tendermint-utils = { git = "https://github.com/cosmos/sp1-ics07-tendermint", rev = "f67f5fec9423a4744092ee98b62bc60e3354f223", default-features = false } +serde = { version = "1.0", default-features = false } +serde_json = { version = "1.0", default-features = false } +serde_cbor = { version = "0.11", default-features = false } +serde_with = { version = "3.11", default-features = false } +hex = { version = "0.4", default-features = false } +prost = { version = "0.13", default-features = false } +bincode = { version = "1.3", default-features = false } +subtle-encoding = { version = "0.5", default-features = false } -serde = { version = "1.0", default-features = false } -serde_json = { version = "1.0", default-features = false } -hex = { version = "0.4", default-features = false } -prost = { version = "0.13", default-features = false } +sha2 = { version = "0.10", default-features = false } tokio = { version = "1.0", default-features = false } axum = { version = "0.7", default-features = false } tonic = { version = "0.12", default-features = false } tonic-build = { version = "0.12", default-features = false } +reqwest = { version = "0.12", default-features = false } tracing = { version = "0.1", default-features = false } tracing-subscriber = { version = "0.3", default-features = false } @@ -37,14 +46,39 @@ anyhow = { version = "1.0", default-features = false } async-trait = { version = "0.1", default-features = false } futures = { version = "0.3", default-features = false } clap = { version = "4.5", default-features = false, features = ["std"] } # std feature is required for clap +time = { version = "0.3", default-features = false } +dotenv = { version = "0.15", default-features = false } -tendermint = { version = "0.40", default-features = false } -tendermint-rpc = { version = "0.40", default-features = false } +tendermint = { version = "0.40", default-features = false } +tendermint-rpc = { version = "0.40", default-features = false } +tendermint-light-client-verifier = { version = "0.40", default-features = false } -ibc-core-host-types = { version = "0.56", default-features = false } +ibc-proto = { version = "0.51", default-features = false } +cosmos-sdk-proto = { version = "0.26", default-features = false } -alloy = { version = "0.6", default-features = false } -alloy-contract = "0.6" -alloy-sol-types = "0.8" +ibc-primitives = { version = "0.56", default-features = false } +ibc-client-tendermint = { version = "0.56", default-features = false } +ibc-core-client = { version = "0.56", default-features = false } +ibc-core-host-types = { version = "0.56", default-features = false } +ibc-core-client-types = { version = "0.56", default-features = false } +ibc-core-commitment-types = { version = "0.56", default-features = false } +ibc-core-handler-types = { version = "0.56", default-features = false } +ibc-client-tendermint-types = { version = "0.56", default-features = false } -sp1-sdk = { version = "3.0", default-features = false } +alloy = { version = "0.7", default-features = false } +alloy-contract = { version = "0.7", default-features = false } +alloy-sol-types = { version = "0.8", default-features = false } +alloy-primitives = { version = "0.8", default-features = false } + +sp1-sdk = { version = "3.3", default-features = false } +sp1-zkvm = { version = "3.3", default-features = false } +sp1-helper = { version = "3.3", default-features = false } + +[patch.crates-io] +sha2-v0-9-8 = { git = "https://github.com/sp1-patches/RustCrypto-hashes", package = "sha2", branch = "patch-v0.9.9" } +sha2-v0-10-8 = { git = "https://github.com/sp1-patches/RustCrypto-hashes", package = "sha2", branch = "patch-v0.10.8" } +ed25519-consensus = { git = "https://github.com/sp1-patches/ed25519-consensus", branch = "patch-v2.1.0" } +ecdsa = { git = "https://github.com/sp1-patches/signatures", branch = "patch-ecdsa-v0.16.9" } +curve25519-dalek-ng = { git = "https://github.com/sp1-patches/curve25519-dalek-ng", branch = "patch-v4.1.1" } +curve25519-dalek = { git = "https://github.com/sp1-patches/curve25519-dalek", branch = "patch-curve25519-v4.1.3" } +tiny-keccak = { git = "https://github.com/sp1-patches/tiny-keccak", branch = "patch-v2.0.2" } diff --git a/README.md b/README.md index 0c4af6df..228f18d3 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [codecov]: https://codecov.io/github/cosmos/solidity-ibc-eureka [codecov-badge]: https://codecov.io/github/cosmos/solidity-ibc-eureka/graph/badge.svg?token=lhplGORQxX -This is a work-in-progress IBC Eureka implementation in Solidity. IBC Eureka is a simplified version of the IBC protocol that is encoding agnostic. +This is a work-in-progress IBC Eureka implementation in Solidity. IBC Eureka is a simplified version of the IBC protocol that is encoding agnostic. This project also includes an [SP1](https://github.com/succinctlabs/sp1) based tendermint light client for the Ethereum chain, and a POC relayer implementation. ## Overview @@ -25,8 +25,11 @@ This project is structered as a [foundry](https://getfoundry.sh/) project with t - `abi/`: Contains the ABIs of the contracts needed for end-to-end tests. - `abigen/`: Contains the abi generated go files for the Solidity contracts. - `e2e/`: Contains the end-to-end tests, powered by [interchaintest](https://github.com/strangelove-ventures/interchaintest). -- `relayer/`: Contains the relayer implementation in Rust. -- `packages/`: Contains the Rust packages for the relayer. +- `programs/`: Contains the Rust programs for the project. + - `relayer/`: Contains the relayer implementation. + - `operator/`: Contains the operator for the SP1 light client. + - `sp1-programs/`: Contains the SP1 programs for the light client. +- `packages/`: Contains the Rust packages for the project. ### Contracts @@ -35,11 +38,23 @@ This project is structered as a [foundry](https://getfoundry.sh/) project with t | `ICS26Router.sol` | IBC Eureka router handles sequencing, replay protection, and timeout checks. Passes proofs to `ICS02Client.sol` for verification, and resolves `portId` for app callbacks. Provable IBC storage is stored in this contract. | ✅ | | `ICS02Client.sol` | IBC Eureka light client router resolves `clientId` for proof verification. It also stores the counterparty information for each client. | ✅ | | `ICS20Transfer.sol` | IBC Eureka transfer application to send and receive tokens to/from another Eureka transfer implementation. | ✅ | +| `SP1ICS07Tendermint.sol` | The light client contract, and the entry point for SP1 proofs. | ✅ | | `ICS27Controller.sol` | IBC Eureka interchain accounts controller. | ❌ | | `ICS27Host.sol` | IBC Eureka interchain accounts host. | ❌ | +### SP1 Programs for the Light Client + +| **Programs** | **Description** | **Status** | +|:-------------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:----------:| +| `update-client` | Once the initial client state and consensus state are submitted, future consensus states can be added to the client by submitting IBC Headers. These headers contain all necessary information to run the Comet BFT Light Client protocol. Also supports partial misbehavior check. | ✅ | +| `membership` | As consensus states are added to the client, they can be used for proof verification by relayers wishing to prove packet flow messages against a particular height on the counterparty. This uses the `verify_membership` and `verify_non_membership` methods on the tendermint client. | ✅ | +| `uc-and-membership` | This is a program that combines `update-client` and `membership` to update the client, and prove membership of packet flow messages against the new consensus state. | ✅ | +| `misbehaviour` | In case, the malicious subset of the validators exceeds the trust level of the client; then the client can be deceived into accepting invalid blocks and the connection is no longer secure. The tendermint client has some mitigations in place to prevent this. | ✅ | + + ## Requirements +- [Rust](https://rustup.rs/) - [Foundry](https://book.getfoundry.sh/getting-started/installation) - [Bun](https://bun.sh/) - [Just](https://just.systems/man/en/) @@ -152,10 +167,54 @@ Since there is no meaningful difference in gas costs between plonk and groth16 i Note: These gas benchmarks are with Groth16. +## Run ICS-07 Tendermint Light Client End to End + +1. Set the environment variables by filling in the `.env` file with the following: + + ```sh + cp .env.example .env + ``` + + You need to fill in the `PRIVATE_KEY`, `SP1_PROVER`, `TENDERMINT_RPC_URL`, and `RPC_URL`. You also need the `SP1_PRIVATE_KEY` field if you are using the SP1 prover network. + +2. Deploy the `SP1ICS07Tendermint` contract: + + ```sh + just deploy-sp1-ics07 + ``` + + This will generate the `contracts/script/genesis.json` file which contains the initialization parameters for the contract. And then deploy the contract using `contracts/script/SP1ICS07Tendermint.s.sol`. + If you see the following error, add `--legacy` to the command in the `justfile`: + ```text + Error: Failed to get EIP-1559 fees + ``` + +3. Your deployed contract address will be printed to the terminal. + + ```text + == Return == + 0: address + ``` + + This will be used when you run the operator in step 5. So add this to your `.env` file. + + ```.env + CONTRACT_ADDRESS= + ``` + +4. Run the Tendermint operator. + + To run the operator, you need to select the prover type for SP1. This is set in the `.env` file with the `SP1_PROVER` value (`network|local|mock`). + If you run the operator with the `network` prover, you need to provide your SP1 network private key with `SP1_PRIVATE_KEY=0xyourprivatekey` in `.env`. + + ```sh + RUST_LOG=info cargo run --bin operator --release -- start + ``` + ## License This project is licensed under MIT. ## Acknowledgements -This project was bootstrapped with this [template](https://github.com/PaulRBerg/foundry-template). Implementations of IBC specifications in [solidity](https://github.com/hyperledger-labs/yui-ibc-solidity/), [CosmWasm](https://github.com/srdtrk/cw-ibc-lite), [golang](https://github.com/cosmos/ibc-go), and [rust](https://github.com/cosmos/ibc-rs) were used as references. +This project was bootstrapped with this [template](https://github.com/PaulRBerg/foundry-template). Implementations of IBC specifications in [solidity](https://github.com/hyperledger-labs/yui-ibc-solidity/), [CosmWasm](https://github.com/srdtrk/cw-ibc-lite), [golang](https://github.com/cosmos/ibc-go), and [rust](https://github.com/cosmos/ibc-rs) were used as references. We are also grateful to [unionlabs](https://github.com/unionlabs/union/) for their `08-wasm` ethereum light client implementation for ibc-go. diff --git a/buf.gen.yaml b/buf.gen.yaml index 02ea8078..1e325bc3 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -12,4 +12,4 @@ plugins: opt: paths=source_relative inputs: - - directory: relayer/proto + - directory: programs/relayer/proto diff --git a/buf.yaml b/buf.yaml index ea243103..143dd1df 100644 --- a/buf.yaml +++ b/buf.yaml @@ -1,7 +1,7 @@ # Learn more at https://buf.build/docs/configuration/v2/buf-yaml/ version: v2 modules: - - path: relayer/proto + - path: programs/relayer/proto name: buf.build/cosmos/solidity-ibc-eureka-relayer lint: use: diff --git a/bun.lockb b/bun.lockb index c12d0079427eebc2b347a28b484dfe475092ce67..dd999500c7c425d8c2d00c5e1683fd611393c35b 100755 GIT binary patch delta 10753 zcmeHNd2|%T_U>*nVLA}9Co{}sB@1LsxiJ>ej8> zx0`gmb$ihz*wW(3#bTmR%+3b9hlU{Veh6&&%aASLllnkq1wvhFMI#S52?Iw3V=nIYIGC#gU^F|n zh}J1_H5cGUu5AXhmK~eQvOkz7{}#-~uLp;M3(Lw%%aRH!zkthFf2H1DWR;{S$fLmA zZy1=z@kBc>s;;xftH5k*FL(m&HMNIy0}6`VB@^M;vuNPe7rD!m#+A6o@74`-Pp&8_ z%akM|ZbD%V#<~_a2$`e_<&rdI+T^N&(y@i>&<}Ihj>MR32!@ry!By_E@lccILGZ&8 zYHM)8{wSxWb|E#55t@2cTJ~gUN$F^1bH0nz?C*(1vb+$?yC2p?TaTa4CNQtJ5zPL% z1U;6sz${0C*+0`Nic5I>lo-vwtHH3MrnU_itT?`O%2-yYjMXN9KT02fN!xXKKbY6G zS?6)QE$)IMb62hZ3XH?;FF@w7^zWvH`CP~dklL$Yj>5Un3kH|Pvp;KKAu4!<9l^Xp z3Ct^~E*17j3Hk~W5;eozV~Q)PrW8ss@HVgPOWji+f_Wi}lQeDy^SJ$!HNAW=N5VH? z4(Iq3%u|DlgHYrNA1Ep=D1rxy%L^+D!B3)|Cvc}~D;rbhE-1uS?t#qpVQJa~Q^36P z;_{^NC8cAe6Og%m7W%V6@4zsYjmR5Uj(DypbBAT<8N&FZ z{Mb@Em{$@2=81f>v;ij-Pby3rJ4TWrb42X~v!1)WYI4EE;tJ&20t_Q9>8{m34d#U! zhD<0=qH@om`@FKXaVL})mau*|#30WzsjzH(VY(#kMmsNLGnnn+lsOf8Wrfqqi^o+- zl%s9=ow?fh%fW2V2G+0p$#bG7HEm6!T5lg(>upxPm1v{4(`u77?KK#Q_azQ8Vx)F3 z`E`nuAEca4E;)y)@H>_2JGqp%6uR8Wsd}`Nq#jW7B)MC*Sr<&y%f}%qD~QYK6D9n_n0K9x2VHN$|@68`#F_ECfexd zl)KU;KbQOz<@meIdp#xTcIaV6sk|CBp}izwotUmyCx=`>m;7DI`u3C?;8ahd39iuR zj;8tm%wv|MKB#NYb1SROlpE+&Rd{<4DzOZIhg?YYfi7jAN|ytjavbFZx#VF~735ME zAc_V+O=dOaxEEax5(>dCQx*27C*}L(DfJy_W3W?ghmGbT750p22&8QKAUIF{8&!q4 zl+7J!V~A6I0e-(lo7cyo{s^g`(M9cnNa6uJg&UuR#Aaia!47pFr2ay4mY+k8r5vkE zdE1Aotxna10Ac@lP_ICTX&9tFG}AXveGXUbUo+dI?uEoVpzV>(&lsG2a=#zt`Z$#( zAe&R&g*M(lOpJbk$Ruo{@SZvn68qOgvqBv5V#=|*)YGWrk>Mk|Lrp>^(llU=Iv&!m zv;G4TZU|T^M`xQM@Gc5>$z7;E)TItbx=KYIOb&LKmq21ejEz?s0;xL8sYW4Z^)-5` z(;=~+#1^R=A!(z|vO1J=K~x>?RO5ps2{*E)B9Ed=;VyL^>ex=1WIu=UMKD!II87-? zaJj;H#kk^_H_^wP9Ht|X`qRvyJX5kolE%;nfqCkDT=7gYeH`jgzlFrMO6&yLk8+}1 z@)J}Q0j5nXiJ9AQ)m!NE-G*mcE(9$I9)Xkx$%}VJ z*<_>YXs6nSN-ezE(wpoYcD(%$;!3v&%Jp`tj&Uk2cG?)@R2_J-j>JIleXv6&%87NU zhf&8)LKww5Od(-B4%R6Trc1G|h((Y&qoS8cRMXRvbCRLH4E30(mZo}JC^cJ^!>BsW zDfguMIG4H%SxpOCEL}MfPPtv3YD$D8^+Jyz_#@j4ft}cn-K@R_3CT~7*u#)`?IQBz zP^#+YQcIE8Z$_QJ*p? z&!Mh>gcPdp{?RT}o!~Utqfsf8hvABSCxV0hlju^mMN@U6SXrV=t;Z9SLr@DkFFc?% zbtELUA0!T9v>^{X3aPgUogZ+ON%{VHYUem(i)TeU)RB;QozO?`#n&W`%k6P=IoT-( zP)>?V9zs&85~RXb~wQRrx4^E~h!wGpOu=ULGRMvlBJTwd`L__318k zGwN6abpZ~w9Uj3wA;HgIQ4AKQ23@da=IL3Sl634ADXNp}lCttfy{2l6iNWH}{ zNWAJHNSuU(i_G2e8o^UsTgY^-5T5-QR~$i@9ogr+E{Ug(8iP&IRx+!b!!!X>uDFq3 z!4(I-c$}z*bglM$yQ+zJv0x*`detgO*CJA_gQR6M-na9Rcy!!Sfev#DjNpYECd!j2 zC)=g2Ml}avN0HNyLgFCoAc8+WLtB-W4<}Ggj!S(Vb$okxvcA%kK^t?NaxPuUahYbr zrAFmtYy$J3=AF7;K^vDc6w;~dIf2UX`g)c_2qhcb3; zAS8H`rw?d2ReAaQr>XskmGh8y%aiFTOogyiB!r@9JPZ~?SrjMo5(9ggv= z4%G(}aVo&(#yQl{ko2&_W~_mvE&OBLfzTPP7eg(%D(0PaLl{x8fE;3gI0AmGJ> zaJw)N&XW`XTQ5o$zg2SK3YlK$7oNt`BPK<;5pxIlQVfeX7fGU-y+aV};*ONsg9o#K z^50mdw|a-rfLkpz@>X*sn>1APxiQP+eN%|osk?P8W?tw$I^V0;U(Y=1eR@4J>yOf9 zX5PsHU1sK$kJshvnfnzPJB173$%(q+jhH8%q_;Ek0F!n3dS<;Tdizwp{YK1&P2>O4 zahax(V43p!z>9s%H5_GC0QTlbu4ldpZjhC0gi#t`I)Wz%dzh^z|{Juf!!Sy_F0>A_Pd0K`N%3oReudMu6Ru=d6Us?Ho%Ep{E zIBScne5=T!oOn?FUs<^>bkKu+>B!b(yfSyfAKg4u<15lsTCga<9FKhNgEx)3rWHjp zxBD6&abCys#G6~zE%Fd8b*az#D&|*_$UAYhyFQ|w=(01GIF%#W%dQ6ov8gIR~49*^sFo58HZiJc!!d~LjI z@CNh&xO@e0Ur#{1FY};m5La5Z;s>SQHfs60yZ3ZlvK^IxiL(B6TMcW;fC97wJOEA& zY{eYl3E)ZKDPSJ(G{7%})xd+m3}7bk5P*eC{GM3`lmivOWS|rn1&jvVz@5M_;4b`) zOB#-g5x`J@pJ)8AwE~?1e$uxGIL3}b{sFiMTn2stt^ij7ek(Z(Gy|uA(*VC#?FQzP zds{$FEv~|W2w)lz4NL(h0wq8JFcv5T#sT*McLNDP5|9kU0r-0df1HxMU=hE6oW%7p zfbWg-z>mOBz#gCh_!ij43)_KV51LRTY_Jo@5xIz?)SDEAB zpMeX&VW1J%3p@`j0hR(U0IPskf!Bc7fg!*hKtJF%U;r=>7zFeM96&aZ1LOj!KpK$F z8+ig3hkygX3SbMc6<7_t3A_c|4rBmFfOmmt1ypV`;}_+%ZxOVybu)IPdU)+&Ponsqq0hgIm2K2vmo;D{DpcH)4?e#8 zRyli4PqR6f9J?aB^UU6;cQj9VSPD{qx z*U2<*S0Ar0u&F*!*e%n(UFpgpnS6I!?8Z0EO*PN{($wpo`_U~sIhDIzkg3n^aY~y^ zZ|+W)z3JfY3^|wr_GHKrbjzNBcH<-U%j;+FwONkcik_LtnaOZ+i$ZJnSd@zj^*kKJ zUyO_oY>^llVDHq$HzU^?tk2Fd^$fh_MTz;dwkUASMzqXqX7*TyZDBV zRSin{|>@z=lAU3lMTIe&4i^U`d5PmfACq>6iind zLgjH}YqZ*pZ|?WnHm|SDx_VOYfaSetrrR3_C@0PIVxz@od{bXrWN&+a_%{vE%hCoO zPCFW{%F8O9YP8smFY2Arb{whyEOQjpusQ64?J7m=wcyXQIeRmd{a!SAZ@O}{1HH6& zfYRE5TK8JAjE~%1&mK5a9@4r2eRcP7EOf-`-MgB;@tn~#1T7gD6}#7wp5AY@8K1oe zB*xag92=RA_H+bATB;O7Q}%+iO`Q)vc@Zsa8Rnlu-k9I`?w{K3sfq<9?q@_#vFazN z*ZvIKAFoWcp(>NIL-Y;+XxiViFkCser<{H|MnT>-4vu{_U zDqnoy4ZR&UiO&|5$%TVLH2HVo)Poodi%8ygi0joTp{(Zn| zGfoBEy{W~t^uXz^P}XCph*A!Ymp$p3gBh|vZ8>PQ8D|9E-uK?A$nwO_zuFf=Z3nH& z$`I=OeY&zfga&5|sa~-_>_kM$ZsE#SRqFw(qT3t2%25FI|4LE&A7X{aKzb5vnqCL0(^-!^5~j1RMEu2*7a z%=UvLGLkbmOGxPyd1#iNB|MNN%ISkcR-19uU`_EG-)#-}Fc{;plaW5>(b+>*r7?m! z9=6#3IAw^mW6ApC#9$oHxl$spof6fjQ7l|6ESR1;yrjGFsd-nYoXP${w{c=iV-r#( z2bt8&-bKEvd+P8PbQ5H0p@OZhY7OWK{Z76OvbtUH(G5yF-q6h9_PK^o8R!TgYDLPr@EPC*$)oz@rNZkF=PHAFN zHS{&xq)Sn>_NXPxI9&12@rHgwHcyBD(%^55CmF{t-umGF(=%^gwvt;!zLvu1%26vm zJmJ@79Lm_88=v^lR_C*d{E!}sVRYLM|E=ARYJT8Rprx&oHvbTAGmb|1o}cX-5?h)j z76_+tijt49rtO&3W*qSt@#e^LktKKR)E$Q~nM;F@S(OzrG~w6)WpxbAKN%E;YU2n< z`=txEcA5RdOZu<~qe^N%X0;nfK4Jz&F8|E6FF>>lKWvYsh~pN!aW*8>ex+u?>SJH( z17Ks?V(8A}>58T&n`y;yt2~#yPyYF!awW|@VYM5_Nm@6|ng5*mC_ih`w0rhyS8|@P z{Hbwb9L^-0PUur>BWq?Tn`5c#$#l7l?uAW%;JLI+NjS@5UxAl0ewA>IIye3DP0QZY z7YZM03ff*ph?xB2XiOl#W-Go*A|~a9baQh;m$ix7eFKW<0f>vMS#sQ|HqIvW!zP z!$zNKyFA~00cz>EIiO~okI6s((YmjEvx?B-(4GTfwB)qaZk(5iPwDr|#d`zKiFT1@ zPo&ZI)9E(jJWO`~uUgWVbgqN~?k&FW!YH(bZwvhXPRoO}M4k_ziD)-Y&CJ{AeeC&{ zm-}H9J%7a0vX%^+acXAp1M$nUcC5K7+EbJ9vg=EyTQZcLSrm0<+>J6AwwH}Dj?=_s z9PnQ@d?mjwrD`5n(w#m$W085DAQL}eyy)y1tFj@RI<_KWc?ty4ro<(UdBZII#l-L( zAGD#Kt?BZURNk6_552Fp4z!6gG*F&9_V4T7Z~XG0BI_9(-%)+eTBDcarJUbH^TPX$ zs$SUtHhnNe*4@hi?F*;!lV%1&3e>na`z)cahoXFl|k P!|T-ZmviyI-)H{=9^)jk delta 11586 zcmeHNd0bT2^?&cd;5-Fn6XvnVqJj(S49tMo88z0TqEQ>861M?|MM0E71)2nm`ipHe z5syiV(WqIPl&n^pnrOADP0~b-v2_Et;1Z1sMzWaNX!<>GQQDtL`{_Tw-yeA&znpvS zJ@?#m&pr3N>%6=6u8GOu3n{_5B-X{_g7O zSik!Im^4w~XJ6yQ*!s}DYFYmiB?t|@&>8_64jKUJtt>Asn^P^!t@2b?mwBolmIR?I z>Z{#_e5bO@# zJ!^q#RvYR=!S4kP11)xYt5fDzmF)n}a$Zlx0#B77RKpM%e0k+ex3^RfiopkhH=&XZ znhMH#c7Srd1GA{-hO#+j>Of0S!81$1pB|vYU^ZLc6}$y>oUW*Q$g9_*`8X}Rn`#AfM(S5fNOMG4~l;cc^IAhyFf9QhLQlPk`k3`7>n(0 z2j%KI7|o8|N1LRi`l-5G$AhxeVgv_E)q?Wi1K}DrJ_a-f)KgVeS(W0cNmDd=f4zMl z_yn}S1IqojgR-6q=wYKGF>|H?pls}mplGje7@-?bRO&9D4aZK3)@EPo_NEkcS@ zIi$DdpBXy87V_-hWuWYz`PF6RtpABVnty{6RV(Tnicw&}lFGR=S>Pmib{PCoxez8f zk~Dr8D9mk8)&Dq%{FfK=~H6TemMM|Jc+(47@_Xsrw zx}-24Y7KOXvq|ahk~jGX!XOBnD89Qxyi84~yW3X~?nRvs#RoaW=Sc~2$){1ra=u(A z+f9Nn7@S0Hnc38W_P0?pRIPy?DZq~kf}PR`KUx#)6d$9OV3+s>DIqR-Xcs{k3pq>z zdRBphi+Q9#hxh@tgt(*_Gew3vP2YleDHH>M9|qAbYR6!#0vli^ci(I? zm@!~5hY*Ljhnm7%(p_DtHOwhKN=grxxSb~VaLHdIWb+}0p+g+f-QB3Qhbj>6GJTAF zKA2*<=Sp$@v?kms&w@i)rNnMAZ3UN2>%w!z&uDUlOG*u(H4#qP7oqnPZQSk-`95%Y zMi=>6aICJdZ*ic>B}46$?IW49US-4 zT+X?l9c*TcB$`(%>Fc(9exz=+A#mL2r7tk%1!X4suQW9PA$exRFbiPZ_vfF};)SOiK*bmtb12xdRKntx5FiW|yRFLEpU!bNWm+Tj3#4-FJjg6zo zzHmA!hoDOj)j5a3vC%&26uRSuj-07RaJCr?&tDBhaS2WC=aLVh?#HMLVR2Jl0=!4Z zgL38HqQst%$Q|o21>!wEo?@bNO{-AKrQ@-=@?I`M0&5rSkj=dX0SQ(Qhy31DFu-YA z%$2I}c9ht8YP7I}l3kL$4;3VJ;lpNpp!?X>yuNis(mc(h%hR zaQwPu!KdK##1-T)W%WmrpsB&Rrlly|LsQdo<QA4vw|^lDnV7v>)6+bt8An(1IOX3oai4j-?Q2yj{Vued;{rkHOsvLOBux z*;RequK*nP!&vC|1UNki@Jc*DO14W5wrN4$Mey$;eJwbTyhY$ z6z>KB=Ku z>1{`^0LQ8YYKwJnQp|NpK@N%>#bRv%2mfLj+&vxg8NChL8lf`+v*zwt$V7+y05~l;5dEfY;9UIT zlmnm)K7mvlcIaSm>}u#!9P$EiyzjBUlN|DU;IvTKh-C=Jw$^675&LZlI2XnAwb4fb z=D4CtZv`I*pF(XI`o9L+7xXYF5`vbuItz})f`+1@2$TgM0Zq^ZqwDT`(qJ*8RKK~8n0kEwF z)`P`U2SmWD24cYk3BV$#k6obVAt6{T{I^D{Q7NmyVyi0PEA_b(?M9b}L?|phLYL}H zyU@D42zois48+B=)OcO$PE?{5LnCPIP;)&?PS7PgQ}%p;UeA=}?$hZMy}mQ$iB8k& znX>$JooCAWX6igsc37#gA}FX2Q|?%%u`1;yE!X)wQ66}X-p-W!SL%Fc%5wAc_A0%- ziX)Yu+ZC{ket%bl>X(IDqi`q6X4L^*fJM68_feK#0x*48muHGIfzSxBf1c3kQ-Ux{ zbQHkdUIg6*p<(m>-<68o9%OuR3NmXy zP@~0HZQ1;XCxRq%AP)V3T8B6vwL?OzR+A3e4J{aLgW9kqUemn`6z3`)<0FYyu0LRo zLBeHG)O?}prkT%_n|*QW#qo!0r-+;Vp9@H$^fBo$JfoX9X)kADyu$ZVuidWto8M{= zSG@_evdL186ScYE!ieBCg%5wZV{V~7~A5*xFUx8Kt zPpH7=aAlBwc*)yOU+uOBbH6~;^9hmdsimAfqp~b0@yl8Qd;nj-1aL}WYkm$q0;~WY z1s(%d(wlpt>Kjnvv#1xS2Id2mz+7ND;06i-KB?aeOavwYlYx7H0YFb65{Lr$NMHu| zh`^_wa{$Nm-+>#zP2gLAk1QShy63acWuP74fCnfBN`Pqq|2Kl)z^OnQ&==?r^a8rUKv}$75H5l_53skt0QnO53TOdZfdjxt zz+ZqTfG2@OU=G0lZI}hj21duKLthr`M_vk3;?6~7i#tYa5*{KE=cBSYe64<8;OW-l9THg6=;fq1*|<-~Du?viUh*)?V} z`(Pv_jgR#D2bm^GoR$S$STBo2Qw|L4ng$cQL(nNv^8u?gLZTA~EYZd{+@1Bm{HAS4 z!E|)XPEF@-QzZ&HSS%GwRCmxSR?{m7ZQ=vehQAL}k3*xQhx=&yo_%BS(J0Hg5$Ktj znwg5ln&3kZ9F zrM$i9Y4osZ#;8{VVEmZ0KR%;;uq#Z}rJC8_L}LzHqQ1N97Fu*zk$n7U?O_Xkp!)4$ zo89>S+p;Y2rt`jKGPJ={JZa-*!F^Hhy-|~O^8#9|Xu+gRGW9%?FO8F_{D>vWxH<58 zY4o?-C;p`ca#@f=wCr36ze8WUHfjl7ZBaxo zO+Bha8Q=EvlM^>Tn;36}logSXkuEGJSF4g`+yN*V=p1vv`%NC&vr;ij1TFLj!OM%D z`uNF?{wMy97Ki4Sw`fJH5{2st>WBRqArJR?byeLGT{FCMjNWcl#4B_ZQpV+mbe|Q~ zjpgnqRVmdsSE>6^+xN^uv){Pe(C#a|{9gOSSJ7V&$*a_OR1rU+moQ7?X2jyLazf41 z3txg%rnXY*EbYe4hl+kZ3!7IL-Ytn7N^rAa+;tdtdR+Z5dFyrbf zJw++CsvQ}rcBFrW#-6Zb8P_Jxzxa#phYtD8y{+yYB(ttqPw&t3Zf<$L_x);Db)bE; z?u3$MT&#F0e$q47mwwy(cDr%);?mcJ$ESyyGjHpBkuIK4qVKpL)%cziO>rmlvC%3{ zDp7aXXz9oi@YE98c+wVSe5KBCJ=f>{#li4|E!D=mO=zMkCl`HB3TdTBK31ZPdl;{k zy>xI-=#Frh$t!^b`X%i|`;d4ojZXOMrQUUwL6gz02eg`kr0G5s*k;MP<7`wv-7$x$ z^JE3#G_P%`-S`xLC@^PENRLsR2s7AU)S9z5TYH}1U! zaBBvZ1v-Tq+H_hGm(p(IuQa6hsxy{o<4TMDk2|A}cDeqLBtDpdiK2_zj?a>#&nQvf zEyeS+MH$ydf<9jy6p>h&rOplx!%>d*ol&xkYbMPHCpEwO$m{>3y9;j=<4Vk=m#1{Z zmybW7w;+-_DCVppnG-4dY<{$UW2WJ!MS+I~tewUQl=n2miW6zWS&Mj*-aZ!=kJ;jY zm88W^pV9Ff{^U9qs>-Tfa3s;-bCzi1Hcd?QjrzvsbMNcQ5ZszF8u&@1q{)hvRQZV_ zcF@stO7y=|tQw(28<%iC``xmYP3E)Ns(K`H90~i8KgR#h^ib8gNU@eyoYRNbv}>kF zsY&$JIjdc}$AilsH5bMm8Ggg1&jhRY9e&No6oM0J+IdT~ab?Igtz*IUyVk#~cZEka zf!h)MJ+ZrrPM+uUK-(!r{2PUSI-plXiWa=a1)=qQ8j=Sr^%Mq)&L3r}-+a%L`%|0v zISpx7a33J!LU=YjV_XBeSa_|v=&^~5VT~Ty#!aI8&y>}!jNjOJdy3D|jZYPE6V+eP z6phx(S>K@xGc#@nq(~sm9oN z0bLn1_M*jZTt4!hWW8svU-LT<&<_#Db)^1jdEb0}UswlP^t?6JN>5+3Mj6+QvPXW< zZhb1U1_C&G@qSxM7ccS-#a})|#9gvv8F!8zYx+rPM^tl~Dy*hA<5tq6Zv~uN^X!IT zwCMS#kt#0P#8TRLNf8&)wo5iC*G`{ZDvr8Cw#(L1)>ky|vLZ5)dwSB8|7lvS6`FC3_SG)(i(%}6OL@}@gdsy%aNda7`@w7OTZ-BFxX>?z90 zwA$R6j?B#T9IMBZlU-=H78cplJvLibX0g?38~i;=p4=HI#%}KV+4X_-cN*hA9YPx+ z<+uV$ex8CVs3TrT-Fr1|5G)F(VDifPPVvHbRGCb=9m8fd_e3= diff --git a/contracts/light-clients/ISP1ICS07Tendermint.sol b/contracts/light-clients/ISP1ICS07Tendermint.sol new file mode 100644 index 000000000..5b2d1d23 --- /dev/null +++ b/contracts/light-clients/ISP1ICS07Tendermint.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +import { ILightClient } from "../interfaces/ILightClient.sol"; +import { IICS07TendermintMsgs } from "./msgs/IICS07TendermintMsgs.sol"; +import { ISP1Verifier } from "@sp1-contracts/ISP1Verifier.sol"; + +/// @title ISP1ICS07Tendermint +/// @notice ISP1ICS07Tendermint is the interface for the ICS07 Tendermint light client +interface ISP1ICS07Tendermint is ILightClient { + /// @notice Immutable update client program verification key. + /// @return The verification key for the update client program. + function UPDATE_CLIENT_PROGRAM_VKEY() external view returns (bytes32); + + /// @notice Immutable membership program verification key. + /// @return The verification key for the membership program. + function MEMBERSHIP_PROGRAM_VKEY() external view returns (bytes32); + + /// @notice Immutable update client and membership program verification key. + /// @return The verification key for the update client and membership program. + function UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY() external view returns (bytes32); + + /// @notice Immutable misbehaviour program verification key. + /// @return The verification key for the misbehaviour program. + function MISBEHAVIOUR_PROGRAM_VKEY() external view returns (bytes32); + + /// @notice Immutable SP1 verifier contract address. + /// @return The SP1 verifier contract. + function VERIFIER() external view returns (ISP1Verifier); + + /// @notice Constant allowed clock drift in seconds. + /// @return The allowed clock drift in seconds. + function ALLOWED_SP1_CLOCK_DRIFT() external view returns (uint16); + + /// @notice Returns the client state. + /// @return The client state. + function getClientState() external view returns (IICS07TendermintMsgs.ClientState memory); + + /// @notice Returns the consensus state keccak256 hash at the given revision height. + /// @param revisionHeight The revision height. + /// @return The consensus state at the given revision height. + function getConsensusStateHash(uint32 revisionHeight) external view returns (bytes32); +} diff --git a/contracts/light-clients/SP1ICS07Tendermint.sol b/contracts/light-clients/SP1ICS07Tendermint.sol new file mode 100644 index 000000000..bef89e85 --- /dev/null +++ b/contracts/light-clients/SP1ICS07Tendermint.sol @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +import { IICS07TendermintMsgs } from "./msgs/IICS07TendermintMsgs.sol"; +import { IUpdateClientMsgs } from "./msgs/IUpdateClientMsgs.sol"; +import { IMembershipMsgs } from "./msgs/IMembershipMsgs.sol"; +import { IUpdateClientAndMembershipMsgs } from "./msgs/IUcAndMembershipMsgs.sol"; +import { IMisbehaviourMsgs } from "./msgs/IMisbehaviourMsgs.sol"; +import { ISP1ICS07TendermintErrors } from "./errors/ISP1ICS07TendermintErrors.sol"; +import { ISP1ICS07Tendermint } from "./ISP1ICS07Tendermint.sol"; + +import { Paths } from "./utils/Paths.sol"; + +import { ILightClientMsgs } from "../msgs/ILightClientMsgs.sol"; +import { ILightClient } from "../interfaces/ILightClient.sol"; + +import { ISP1Verifier } from "@sp1-contracts/ISP1Verifier.sol"; +import { SP1Verifier as SP1VerifierPlonk } from "@sp1-contracts/v3.0.0/SP1VerifierPlonk.sol"; +import { SP1Verifier as SP1VerifierGroth16 } from "@sp1-contracts/v3.0.0/SP1VerifierGroth16.sol"; + +import { Multicall } from "@openzeppelin/utils/Multicall.sol"; +import { TransientSlot } from "@openzeppelin/utils/TransientSlot.sol"; + +/// @title SP1 ICS07 Tendermint Light Client +/// @author srdtrk +/// @notice This contract implements an ICS07 IBC tendermint light client using SP1. +/// @custom:poc This is a proof of concept implementation. +contract SP1ICS07Tendermint is + IICS07TendermintMsgs, + IUpdateClientMsgs, + IMembershipMsgs, + IUpdateClientAndMembershipMsgs, + IMisbehaviourMsgs, + ISP1ICS07TendermintErrors, + ILightClientMsgs, + ISP1ICS07Tendermint, + Multicall +{ + using TransientSlot for *; + + /// @inheritdoc ISP1ICS07Tendermint + bytes32 public immutable UPDATE_CLIENT_PROGRAM_VKEY; + /// @inheritdoc ISP1ICS07Tendermint + bytes32 public immutable MEMBERSHIP_PROGRAM_VKEY; + /// @inheritdoc ISP1ICS07Tendermint + bytes32 public immutable UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY; + /// @inheritdoc ISP1ICS07Tendermint + bytes32 public immutable MISBEHAVIOUR_PROGRAM_VKEY; + /// @inheritdoc ISP1ICS07Tendermint + ISP1Verifier public immutable VERIFIER; + + /// @notice The ICS07Tendermint client state + ClientState private clientState; + /// @notice The mapping from height to consensus state keccak256 hashes. + mapping(uint32 height => bytes32 hash) private consensusStateHashes; + + /// @notice Allowed clock drift in seconds. + /// @inheritdoc ISP1ICS07Tendermint + uint16 public constant ALLOWED_SP1_CLOCK_DRIFT = 3000; // 3000 seconds + + /// @notice The constructor sets the program verification key and the initial client and consensus states. + /// @param updateClientProgramVkey The verification key for the update client program. + /// @param membershipProgramVkey The verification key for the verify (non)membership program. + /// @param updateClientAndMembershipProgramVkey The verification key for the update client and membership program. + /// @param misbehaviourProgramVkey The verification key for the misbehaviour program. + /// @param _clientState The encoded initial client state. + /// @param _consensusState The encoded initial consensus state. + constructor( + bytes32 updateClientProgramVkey, + bytes32 membershipProgramVkey, + bytes32 updateClientAndMembershipProgramVkey, + bytes32 misbehaviourProgramVkey, + bytes memory _clientState, + bytes32 _consensusState + ) { + UPDATE_CLIENT_PROGRAM_VKEY = updateClientProgramVkey; + MEMBERSHIP_PROGRAM_VKEY = membershipProgramVkey; + UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY = updateClientAndMembershipProgramVkey; + MISBEHAVIOUR_PROGRAM_VKEY = misbehaviourProgramVkey; + + clientState = abi.decode(_clientState, (ClientState)); + consensusStateHashes[clientState.latestHeight.revisionHeight] = _consensusState; + + if (clientState.zkAlgorithm == SupportedZkAlgorithm.Groth16) { + VERIFIER = new SP1VerifierGroth16(); + } else if (clientState.zkAlgorithm == SupportedZkAlgorithm.Plonk) { + VERIFIER = new SP1VerifierPlonk(); + } else { + revert UnknownZkAlgorithm(uint8(clientState.zkAlgorithm)); + } + + require( + clientState.trustingPeriod <= clientState.unbondingPeriod, + TrustingPeriodTooLong(clientState.trustingPeriod, clientState.unbondingPeriod) + ); + } + + /// @inheritdoc ISP1ICS07Tendermint + function getClientState() public view returns (ClientState memory) { + return clientState; + } + + /// @inheritdoc ISP1ICS07Tendermint + function getConsensusStateHash(uint32 revisionHeight) public view returns (bytes32) { + bytes32 hash = consensusStateHashes[revisionHeight]; + require(hash != 0, ConsensusStateNotFound()); + return hash; + } + + /// @notice The entrypoint for updating the client. + /// @dev This function verifies the public values and forwards the proof to the SP1 verifier. + /// @param updateMsg The encoded update message. + /// @return The result of the update. + /// @inheritdoc ILightClient + function updateClient(bytes calldata updateMsg) public notFrozen returns (UpdateResult) { + MsgUpdateClient memory msgUpdateClient = abi.decode(updateMsg, (MsgUpdateClient)); + require( + msgUpdateClient.sp1Proof.vKey == UPDATE_CLIENT_PROGRAM_VKEY, + VerificationKeyMismatch(UPDATE_CLIENT_PROGRAM_VKEY, msgUpdateClient.sp1Proof.vKey) + ); + + UpdateClientOutput memory output = abi.decode(msgUpdateClient.sp1Proof.publicValues, (UpdateClientOutput)); + + validateUpdateClientPublicValues(output); + + UpdateResult updateResult = checkUpdateResult(output); + if (updateResult == UpdateResult.Update) { + // adding the new consensus state to the mapping + if (output.newHeight.revisionHeight > clientState.latestHeight.revisionHeight) { + clientState.latestHeight = output.newHeight; + } + consensusStateHashes[output.newHeight.revisionHeight] = keccak256(abi.encode(output.newConsensusState)); + } else if (updateResult == UpdateResult.Misbehaviour) { + clientState.isFrozen = true; + } else if (updateResult == UpdateResult.NoOp) { + return UpdateResult.NoOp; + } + + verifySP1Proof(msgUpdateClient.sp1Proof); + + return updateResult; + } + + /// @notice The entrypoint for verifying (non)membership proof. + /// @param msgMembership The membership message. + /// @return timestamp The timestamp of the trusted consensus state. + /// @inheritdoc ILightClient + function membership(MsgMembership calldata msgMembership) public notFrozen returns (uint256 timestamp) { + if (msgMembership.proof.length == 0) { + // cached proof + return getCachedKvPair( + msgMembership.proofHeight.revisionHeight, KVPair(msgMembership.path, msgMembership.value) + ); + } + + MembershipProof memory membershipProof = abi.decode(msgMembership.proof, (MembershipProof)); + if (membershipProof.proofType == MembershipProofType.SP1MembershipProof) { + return handleSP1MembershipProof( + msgMembership.proofHeight, membershipProof.proof, msgMembership.path, msgMembership.value + ); + } else if (membershipProof.proofType == MembershipProofType.SP1MembershipAndUpdateClientProof) { + return handleSP1UpdateClientAndMembership( + msgMembership.proofHeight, membershipProof.proof, msgMembership.path, msgMembership.value + ); + } + + // unreachable + revert UnknownMembershipProofType(uint8(membershipProof.proofType)); + } + + /// @notice The entrypoint for misbehaviour. + /// @inheritdoc ILightClient + function misbehaviour(bytes calldata misbehaviourMsg) public notFrozen { + MsgSubmitMisbehaviour memory msgSubmitMisbehaviour = abi.decode(misbehaviourMsg, (MsgSubmitMisbehaviour)); + require( + msgSubmitMisbehaviour.sp1Proof.vKey == MISBEHAVIOUR_PROGRAM_VKEY, + VerificationKeyMismatch(MISBEHAVIOUR_PROGRAM_VKEY, msgSubmitMisbehaviour.sp1Proof.vKey) + ); + + MisbehaviourOutput memory output = abi.decode(msgSubmitMisbehaviour.sp1Proof.publicValues, (MisbehaviourOutput)); + + validateMisbehaviourOutput(output); + + verifySP1Proof(msgSubmitMisbehaviour.sp1Proof); + + // If the misbehaviour and proof is valid, the client needs to be frozen + clientState.isFrozen = true; + } + + /// @notice The entrypoint for upgrading the client. + /// @inheritdoc ILightClient + function upgradeClient(bytes calldata) public view notFrozen { + // TODO: Not yet implemented. (#78) + revert FeatureNotSupported(); + } + + /// @notice Handles the `SP1MembershipProof` proof type. + /// @param proofHeight The height of the proof. + /// @param proofBytes The encoded proof. + /// @param kvPath The path of the key-value pair. + /// @param kvValue The value of the key-value pair. + /// @return The timestamp of the trusted consensus state. + function handleSP1MembershipProof( + Height calldata proofHeight, + bytes memory proofBytes, + bytes[] calldata kvPath, + bytes calldata kvValue + ) + private + returns (uint256) + { + SP1MembershipProof memory proof = abi.decode(proofBytes, (SP1MembershipProof)); + require( + proof.sp1Proof.vKey == MEMBERSHIP_PROGRAM_VKEY, + VerificationKeyMismatch(MEMBERSHIP_PROGRAM_VKEY, proof.sp1Proof.vKey) + ); + + MembershipOutput memory output = abi.decode(proof.sp1Proof.publicValues, (MembershipOutput)); + require( + output.kvPairs.length > 0 && output.kvPairs.length <= 256, LengthIsOutOfRange(output.kvPairs.length, 1, 256) + ); + + { + // loop through the key-value pairs and validate them + bool found = false; + for (uint8 i = 0; i < output.kvPairs.length; i++) { + if (!Paths.equal(output.kvPairs[i].path, kvPath)) { + continue; + } + + bytes memory value = output.kvPairs[i].value; + require( + value.length == kvValue.length && keccak256(value) == keccak256(kvValue), + MembershipProofValueMismatch(kvValue, value) + ); + + found = true; + break; + } + require(found, MembershipProofKeyNotFound(kvPath)); + } + + validateMembershipOutput(output.commitmentRoot, proofHeight.revisionHeight, proof.trustedConsensusState); + + verifySP1Proof(proof.sp1Proof); + + // We avoid the cost of caching for single kv pairs, as reusing the proof is not necessary + if (output.kvPairs.length > 1) { + cacheKvPairs(proofHeight.revisionHeight, output.kvPairs, proof.trustedConsensusState.timestamp); + } + return proof.trustedConsensusState.timestamp; + } + + /// @notice The entrypoint for handling the `SP1MembershipAndUpdateClientProof` proof type. + /// @dev This function verifies the public values and forwards the proof to the SP1 verifier. + /// @param proofHeight The height of the proof. + /// @param proofBytes The encoded proof. + /// @param kvPath The path of the key-value pair. + /// @param kvValue The value of the key-value pair. + /// @return The timestamp of the new consensus state. + // solhint-disable-next-line code-complexity + function handleSP1UpdateClientAndMembership( + Height calldata proofHeight, + bytes memory proofBytes, + bytes[] calldata kvPath, + bytes calldata kvValue + ) + private + returns (uint256) + { + // validate proof and deserialize output + UcAndMembershipOutput memory output; + { + SP1MembershipAndUpdateClientProof memory proof = abi.decode(proofBytes, (SP1MembershipAndUpdateClientProof)); + require( + proof.sp1Proof.vKey == UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY, + VerificationKeyMismatch(UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY, proof.sp1Proof.vKey) + ); + + output = abi.decode(proof.sp1Proof.publicValues, (UcAndMembershipOutput)); + require( + output.kvPairs.length > 0 && output.kvPairs.length <= 256, + LengthIsOutOfRange(output.kvPairs.length, 1, 256) + ); + + require( + proofHeight.revisionHeight == output.updateClientOutput.newHeight.revisionHeight + && proofHeight.revisionNumber == output.updateClientOutput.newHeight.revisionNumber, + ProofHeightMismatch( + proofHeight.revisionNumber, + proofHeight.revisionHeight, + output.updateClientOutput.newHeight.revisionNumber, + output.updateClientOutput.newHeight.revisionHeight + ) + ); + + validateUpdateClientPublicValues(output.updateClientOutput); + + verifySP1Proof(proof.sp1Proof); + } + + // check update result + { + UpdateResult updateResult = checkUpdateResult(output.updateClientOutput); + if (updateResult == UpdateResult.Update) { + // adding the new consensus state to the mapping + if (proofHeight.revisionHeight > clientState.latestHeight.revisionHeight) { + clientState.latestHeight = proofHeight; + } + consensusStateHashes[proofHeight.revisionHeight] = + keccak256(abi.encode(output.updateClientOutput.newConsensusState)); + } else if (updateResult == UpdateResult.Misbehaviour) { + clientState.isFrozen = true; + revert CannotHandleMisbehavior(); + } // else: NoOp + } + + // loop through the key-value pairs and validate them + { + bool found = false; + for (uint8 i = 0; i < output.kvPairs.length; i++) { + if (!Paths.equal(output.kvPairs[i].path, kvPath)) { + continue; + } + + bytes memory value = output.kvPairs[i].value; + require( + value.length == kvValue.length && keccak256(value) == keccak256(kvValue), + MembershipProofValueMismatch(kvValue, value) + ); + + found = true; + break; + } + require(found, MembershipProofKeyNotFound(kvPath)); + } + + validateMembershipOutput( + output.updateClientOutput.newConsensusState.root, + output.updateClientOutput.newHeight.revisionHeight, + output.updateClientOutput.newConsensusState + ); + + // We avoid the cost of caching for single kv pairs, as reusing the proof is not necessary + if (output.kvPairs.length > 1) { + cacheKvPairs( + proofHeight.revisionHeight, output.kvPairs, output.updateClientOutput.newConsensusState.timestamp + ); + } + return output.updateClientOutput.newConsensusState.timestamp; + } + + /// @notice Validates the MembershipOutput public values. + /// @param outputCommitmentRoot The commitment root of the output. + /// @param proofHeight The height of the proof. + /// @param trustedConsensusState The trusted consensus state + function validateMembershipOutput( + bytes32 outputCommitmentRoot, + uint32 proofHeight, + ConsensusState memory trustedConsensusState + ) + private + view + { + bytes32 trustedConsensusStateHash = keccak256(abi.encode(trustedConsensusState)); + bytes32 storedConsensusStateHash = getConsensusStateHash(proofHeight); + require( + trustedConsensusStateHash == storedConsensusStateHash, + ConsensusStateHashMismatch(storedConsensusStateHash, trustedConsensusStateHash) + ); + + require( + outputCommitmentRoot == trustedConsensusState.root, + ConsensusStateRootMismatch(trustedConsensusState.root, outputCommitmentRoot) + ); + } + + /// @notice Validates the SP1ICS07UpdateClientOutput public values. + /// @param output The public values. + function validateUpdateClientPublicValues(UpdateClientOutput memory output) private view { + validateClientStateAndTime(output.clientState, output.time); + + bytes32 outputConsensusStateHash = keccak256(abi.encode(output.trustedConsensusState)); + bytes32 storedConsensusStateHash = getConsensusStateHash(output.trustedHeight.revisionHeight); + require( + outputConsensusStateHash == storedConsensusStateHash, + ConsensusStateHashMismatch(storedConsensusStateHash, outputConsensusStateHash) + ); + } + + /// @notice Validates the SP1ICS07MisbehaviourOutput public values. + /// @param output The public values. + function validateMisbehaviourOutput(MisbehaviourOutput memory output) private view { + validateClientStateAndTime(output.clientState, output.time); + + // make sure the trusted consensus state from header 1 is known (trusted) by matching it with the the one in the + // mapping + bytes32 outputConsensusStateHash1 = keccak256(abi.encode(output.trustedConsensusState1)); + bytes32 storedConsensusStateHash1 = getConsensusStateHash(output.trustedHeight1.revisionHeight); + require( + outputConsensusStateHash1 == storedConsensusStateHash1, + ConsensusStateHashMismatch(storedConsensusStateHash1, outputConsensusStateHash1) + ); + + // make sure the trusted consensus state from header 2 is known (trusted) by matching it with the the one in the + // mapping + bytes32 outputConsensusStateHash2 = keccak256(abi.encode(output.trustedConsensusState2)); + bytes32 storedConsensusStateHash2 = getConsensusStateHash(output.trustedHeight2.revisionHeight); + require( + outputConsensusStateHash2 == storedConsensusStateHash2, + ConsensusStateHashMismatch(storedConsensusStateHash2, outputConsensusStateHash2) + ); + } + + /// @notice Validates the client state and time. + /// @dev This function does not check the equality of the latest height and isFrozen. + /// @param publicClientState The public client state. + /// @param time The time. + function validateClientStateAndTime(ClientState memory publicClientState, uint64 time) private view { + require(time <= block.timestamp, ProofIsInTheFuture(block.timestamp, time)); + require(block.timestamp - time <= ALLOWED_SP1_CLOCK_DRIFT, ProofIsTooOld(block.timestamp, time)); + + // Check client state equality + // NOTE: We do not check the equality of latest height and isFrozen + require( + bytes(publicClientState.chainId).length == bytes(clientState.chainId).length + && keccak256(bytes(publicClientState.chainId)) == keccak256(bytes(clientState.chainId)), + ChainIdMismatch(clientState.chainId, publicClientState.chainId) + ); + require( + publicClientState.trustLevel.numerator == clientState.trustLevel.numerator + && publicClientState.trustLevel.denominator == clientState.trustLevel.denominator, + TrustThresholdMismatch( + clientState.trustLevel.numerator, + clientState.trustLevel.denominator, + publicClientState.trustLevel.numerator, + publicClientState.trustLevel.denominator + ) + ); + require( + publicClientState.trustingPeriod == clientState.trustingPeriod, + TrustingPeriodMismatch(clientState.trustingPeriod, publicClientState.trustingPeriod) + ); + require( + publicClientState.unbondingPeriod == clientState.unbondingPeriod, + UnbondingPeriodMismatch(clientState.unbondingPeriod, publicClientState.unbondingPeriod) + ); + } + + /// @notice Checks for basic misbehaviour. + /// @dev This function checks if the consensus state at the new height is different than the one in the mapping + /// @dev or if the timestamp is not increasing. + /// @dev If any of these conditions are met, it returns a Misbehaviour UpdateResult. + /// @param output The public values of the update client program. + /// @return The result of the update. + function checkUpdateResult(UpdateClientOutput memory output) private view returns (UpdateResult) { + bytes32 consensusStateHash = consensusStateHashes[output.newHeight.revisionHeight]; + if (consensusStateHash == bytes32(0)) { + // No consensus state at the new height, so no misbehaviour + return UpdateResult.Update; + } else if ( + consensusStateHash != keccak256(abi.encode(output.newConsensusState)) + || output.trustedConsensusState.timestamp >= output.newConsensusState.timestamp + ) { + // The consensus state at the new height is different than the one in the mapping + // or the timestamp is not increasing + return UpdateResult.Misbehaviour; + } else { + // The consensus state at the new height is the same as the one in the mapping + return UpdateResult.NoOp; + } + } + + /// @notice Verifies the SP1 proof + /// @param proof The SP1 proof. + function verifySP1Proof(SP1Proof memory proof) private view { + VERIFIER.verifyProof(proof.vKey, proof.publicValues, proof.proof); + } + + /// @notice Caches the key-value pairs to the transient storage with the timestamp. + /// @param proofHeight The height of the proof. + /// @param kvPairs The key-value pairs. + /// @param timestamp The timestamp of the trusted consensus state. + /// @dev WARNING: Transient store is not reverted even if a message within a transaction reverts. + /// @dev WARNING: This function must be called after all proof and validation checks. + function cacheKvPairs(uint32 proofHeight, KVPair[] memory kvPairs, uint256 timestamp) private { + for (uint8 i = 0; i < kvPairs.length; i++) { + bytes32 kvPairHash = keccak256(abi.encode(proofHeight, kvPairs[i])); + kvPairHash.asUint256().tstore(timestamp); + } + } + + /// @notice Gets the timestamp of the cached key-value pair from the transient storage. + /// @param proofHeight The height of the proof. + /// @param kvPair The key-value pair. + /// @return The timestamp of the cached key-value pair. + function getCachedKvPair(uint32 proofHeight, KVPair memory kvPair) private view returns (uint256) { + bytes32 kvPairHash = keccak256(abi.encode(proofHeight, kvPair)); + uint256 timestamp = kvPairHash.asUint256().tload(); + require(timestamp != 0, KeyValuePairNotInCache(kvPair.path, kvPair.value)); + return timestamp; + } + + modifier notFrozen() { + require(!clientState.isFrozen, FrozenClientState()); + _; + } + + /// @notice A dummy function to generate the ABI for the parameters. + /// @param o1 The MembershipOutput. + /// @param o2 The UcAndMembershipOutput. + /// @param o3 The MsgUpdateClient. + /// @param o4 The MembershipProof. + /// @param o5 The SP1MembershipProof. + /// @param o6 The SP1MembershipAndUpdateClientProof. + /// @param o7 The MisbehaviourOutput. + /// @param o8 The MsgSubmitMisbehaviour. + function abiPublicTypes( + MembershipOutput memory o1, + UcAndMembershipOutput memory o2, + MsgUpdateClient memory o3, + MembershipProof memory o4, + SP1MembershipProof memory o5, + SP1MembershipAndUpdateClientProof memory o6, + MisbehaviourOutput memory o7, + MsgSubmitMisbehaviour memory o8 + ) + public + pure + // solhint-disable-next-line no-empty-blocks + { + // This is a dummy function to generate the ABI for outputs + // so that it can be used in the SP1 verifier contract. + // The function is not used in the contract. + } +} diff --git a/contracts/light-clients/errors/ISP1ICS07TendermintErrors.sol b/contracts/light-clients/errors/ISP1ICS07TendermintErrors.sol new file mode 100644 index 000000000..e787df05 --- /dev/null +++ b/contracts/light-clients/errors/ISP1ICS07TendermintErrors.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +interface ISP1ICS07TendermintErrors { + /// @notice The error that is returned when the verification key does not match the expected value. + /// @param expected The expected verification key. + /// @param actual The actual verification key. + error VerificationKeyMismatch(bytes32 expected, bytes32 actual); + + /// @notice The error that is returned when the client state is frozen. + error FrozenClientState(); + + /// @notice The error that is returned when a proof is in the future. + /// @param now The current timestamp in seconds. + /// @param proofTimestamp The timestamp in the proof in seconds. + error ProofIsInTheFuture(uint256 now, uint256 proofTimestamp); + + /// @notice The error that is returned when a proof is too old. + /// @param now The current timestamp in seconds. + /// @param proofTimestamp The timestamp in the proof in seconds. + error ProofIsTooOld(uint256 now, uint256 proofTimestamp); + + /// @notice The error that is returned when the chain ID does not match the expected value. + /// @param expected The expected chain ID. + /// @param actual The actual chain ID. + error ChainIdMismatch(string expected, string actual); + + /// @notice The error that is returned when the trust threshold does not match the expected value. + /// @param expectedNumerator The expected numerator of the trust threshold. + /// @param expectedDenominator The expected denominator of the trust threshold. + /// @param actualNumerator The actual numerator of the trust threshold. + /// @param actualDenominator The actual denominator of the trust threshold. + error TrustThresholdMismatch( + uint256 expectedNumerator, uint256 expectedDenominator, uint256 actualNumerator, uint256 actualDenominator + ); + + /// @notice The error that is returned when the trusting period does not match the expected value. + /// @param expected The expected trusting period in seconds. + /// @param actual The actual trusting period in seconds. + error TrustingPeriodMismatch(uint256 expected, uint256 actual); + + /// @notice The error that is returned when the unbonding period does not match the expected value. + /// @param expected The expected unbonding period in seconds. + /// @param actual The actual unbonding period in seconds. + error UnbondingPeriodMismatch(uint256 expected, uint256 actual); + + /// @notice The error that is returned when the trusting period is longer than the unbonding period. + /// @param trustingPeriod The trusting period in seconds. + /// @param unbondingPeriod The unbonding period in seconds. + error TrustingPeriodTooLong(uint256 trustingPeriod, uint256 unbondingPeriod); + + /// @notice The error that is returned when the consensus state hash does not match the expected value. + /// @param expected The expected consensus state hash. + /// @param actual The actual consensus state hash. + error ConsensusStateHashMismatch(bytes32 expected, bytes32 actual); + + /// @notice The error that is returned when the consensus state is not found. + error ConsensusStateNotFound(); + + /// @notice The error that is returned when the length of a value is out of range. + /// @param length The length of the value. + /// @param min The minimum length of the value. + /// @param max The maximum length of the value. + error LengthIsOutOfRange(uint256 length, uint256 min, uint256 max); + + /// @notice The error that is returned when the key-value pair's value does not match the expected value. + /// @param expected The expected value. + /// @param actual The actual value. + error MembershipProofValueMismatch(bytes expected, bytes actual); + + /// @notice The error that is returned when the key-value pair's path is not contained in the proof. + /// @param path The path of the key-value pair. + error MembershipProofKeyNotFound(bytes[] path); + + /// @notice The error that is returned when the consensus state root does not match the expected value. + /// @param expected The expected consensus state root. + /// @param actual The actual consensus state root. + error ConsensusStateRootMismatch(bytes32 expected, bytes32 actual); + + /// @notice The error that is returned when the client state does not match the expected value. + /// @param expected The expected client state. + /// @param actual The actual client state. + error ClientStateMismatch(bytes expected, bytes actual); + + /// @notice The error that is returned when the update client and membership program contains misbehavior. + /// @dev Misbehavior cannot be handled in membership handler, so it is returned as an error. + error CannotHandleMisbehavior(); + + /// @notice The error that is returned when the proof height does not match the expected value. + /// @param expectedRevisionNumber The expected revision number. + /// @param expectedRevisionHeight The expected revision height. + /// @param actualRevisionNumber The actual revision number. + /// @param actualRevisionHeight The actual revision height. + error ProofHeightMismatch( + uint64 expectedRevisionNumber, + uint64 expectedRevisionHeight, + uint64 actualRevisionNumber, + uint64 actualRevisionHeight + ); + + /// @notice The error that is returned when the membership proof type is unknown. + /// @param proofType The unknown membership proof type. + error UnknownMembershipProofType(uint8 proofType); + + /// @notice The error that is returned when the zk algorithm is unknown. + /// @param algorithm The unknown zk algorithm. + error UnknownZkAlgorithm(uint8 algorithm); + + /// @notice Returned when the feature is not supported. + error FeatureNotSupported(); + + /// @notice Returned when the membership proof is invalid. + error InvalidMembershipProof(); + + /// @notice Returned when a key-value pair is not in the cache. + /// @param path The path of the key-value pair. + /// @param value The value of the key-value pair. + error KeyValuePairNotInCache(bytes[] path, bytes value); +} diff --git a/contracts/light-clients/msgs/IICS07TendermintMsgs.sol b/contracts/light-clients/msgs/IICS07TendermintMsgs.sol new file mode 100644 index 000000000..54214d31 --- /dev/null +++ b/contracts/light-clients/msgs/IICS07TendermintMsgs.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import { IICS02ClientMsgs } from "../../msgs/IICS02ClientMsgs.sol"; +import { ISP1Msgs } from "./ISP1Msgs.sol"; + +/// @title ICS07 Tendermint Messages +/// @author srdtrk +/// @notice Defines shared types for ICS07Tendermint implementations. +interface IICS07TendermintMsgs is IICS02ClientMsgs, ISP1Msgs { + /// @notice Fraction of validator overlap needed to update header + /// @param numerator Numerator of the fraction + /// @param denominator Denominator of the fraction + struct TrustThreshold { + uint8 numerator; + uint8 denominator; + } + + /// @notice Defines the ICS07Tendermint ClientState for ibc-lite + /// @param chainId Chain ID + /// @param trustLevel Fraction of validator overlap needed to update header + /// @param latestHeight Latest height the client was updated to + /// @param trustingPeriod duration of the period since the LatestTimestamp during which the + /// submitted headers are valid for upgrade in seconds. + /// @param unbondingPeriod duration of the staking unbonding period in seconds + /// @param isFrozen whether or not client is frozen (due to misbehavior) + /// @param zkAlgorithm The zk algorithm supported by this contract. + struct ClientState { + string chainId; + TrustThreshold trustLevel; + Height latestHeight; + uint32 trustingPeriod; + uint32 unbondingPeriod; + bool isFrozen; + SupportedZkAlgorithm zkAlgorithm; + } + + /// @notice Defines the Tendermint light client's consensus state at some height. + /// @param timestamp timestamp that corresponds to the counterparty block height + /// in which the ConsensusState was generated. + /// @param root commitment root (i.e app hash) + /// @param nextValidatorsHash next validators hash + struct ConsensusState { + uint64 timestamp; + bytes32 root; + bytes32 nextValidatorsHash; + } +} diff --git a/contracts/light-clients/msgs/IMembershipMsgs.sol b/contracts/light-clients/msgs/IMembershipMsgs.sol new file mode 100644 index 000000000..5463125a --- /dev/null +++ b/contracts/light-clients/msgs/IMembershipMsgs.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import { ISP1Msgs } from "./ISP1Msgs.sol"; +import { IICS07TendermintMsgs } from "./IICS07TendermintMsgs.sol"; + +/// @title Membership Program Messages +/// @author srdtrk +/// @notice Defines shared types for the verify (non)membership program. +interface IMembershipMsgs is ISP1Msgs { + /// @notice The key-value pair used in the verify (non)membership program. + /// @param path The path of the value in the key-value store. + /// @param value The value of the key-value pair. + struct KVPair { + bytes[] path; + bytes value; + } + + /// @notice The public value output for the sp1 verify (non)membership program. + /// @param commitmentRoot The app hash of the header. + /// @param kvPairs The key-value pairs verified by the program. + struct MembershipOutput { + bytes32 commitmentRoot; + KVPair[] kvPairs; + } + + /// @notice The membership proof that can be submitted to the SP1Verifier contract. + /// @param proofType The type of the membership proof. + /// @param proof The membership proof. + struct MembershipProof { + MembershipProofType proofType; + bytes proof; + } + + /// @notice The membership proof for the sp1 verify (non)membership program. + /// @param sp1Proof The sp1 proof for the membership program. + /// @param trustedConsensusState The trusted consensus state that the proof is based on. + struct SP1MembershipProof { + SP1Proof sp1Proof; + IICS07TendermintMsgs.ConsensusState trustedConsensusState; + } + + /// @notice The membership proof for the sp1 verify (non)membership and update client program. + /// @param sp1Proof The sp1 proof for the membership and update client program. + struct SP1MembershipAndUpdateClientProof { + SP1Proof sp1Proof; + } + + /// @notice The type of the membership proof. + enum MembershipProofType { + /// The proof is for the verify membership program. + SP1MembershipProof, + /// The proof is for the verify membership and update client program. + SP1MembershipAndUpdateClientProof + } +} diff --git a/contracts/light-clients/msgs/IMisbehaviourMsgs.sol b/contracts/light-clients/msgs/IMisbehaviourMsgs.sol new file mode 100644 index 000000000..85177a48 --- /dev/null +++ b/contracts/light-clients/msgs/IMisbehaviourMsgs.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import { IICS07TendermintMsgs } from "./IICS07TendermintMsgs.sol"; + +/// @title Misbehaviour Program Messages +/// @author gjermundgaraba +/// @notice Defines shared types for the misbehaviour program. +interface IMisbehaviourMsgs is IICS07TendermintMsgs { + /// @notice The message that is submitted to the misbehaviour function. + /// @param sp1Proof The SP1 proof for updating the client. + struct MsgSubmitMisbehaviour { + SP1Proof sp1Proof; + } + + /// @notice The public value output for the sp1 misbehaviour program. + /// @param clientState The client state that was used to verify the misbehaviour. + /// @param time The time which the misbehaviour was verified in seconds. + /// @param trustedHeight1 The trusted height of header 1 + /// @param trustedHeight2 The trusted height of header 2 + /// @param trustedConsensusState1 The trusted consensus state of header 1 + /// @param trustedConsensusState2 The trusted consensus state of header 2 + struct MisbehaviourOutput { + ClientState clientState; + uint64 time; + Height trustedHeight1; + Height trustedHeight2; + ConsensusState trustedConsensusState1; + ConsensusState trustedConsensusState2; + } +} diff --git a/contracts/light-clients/msgs/ISP1Msgs.sol b/contracts/light-clients/msgs/ISP1Msgs.sol new file mode 100644 index 000000000..49c034a2 --- /dev/null +++ b/contracts/light-clients/msgs/ISP1Msgs.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +/// @title SP1 Messages +interface ISP1Msgs { + /// @notice The SP1 proof that can be submitted to the SP1Verifier contract. + /// @param vKey The verification key for the program. + /// @param publicValues The public values for the program. + /// @param proof The proof for the program. + struct SP1Proof { + bytes32 vKey; + bytes publicValues; + bytes proof; + } + + /// @notice Defines the supported zk algorithms + /// @param Groth16 Groth16 zk algorithm + /// @param Plonk Plonk zk algorithm + enum SupportedZkAlgorithm { + Groth16, + Plonk + } +} diff --git a/contracts/light-clients/msgs/IUcAndMembershipMsgs.sol b/contracts/light-clients/msgs/IUcAndMembershipMsgs.sol new file mode 100644 index 000000000..68f98f90 --- /dev/null +++ b/contracts/light-clients/msgs/IUcAndMembershipMsgs.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import { IUpdateClientMsgs } from "./IUpdateClientMsgs.sol"; +import { IMembershipMsgs } from "./IMembershipMsgs.sol"; + +/// @title Update Client and Membership Program Messages +/// @author srdtrk +/// @notice Defines shared types for the update client and membership program. +interface IUpdateClientAndMembershipMsgs is IUpdateClientMsgs { + /// @notice The public value output for the sp1 update client and membership program. + /// @param updateClientOutput The output of the update client program. + /// @param kvPairs The key-value pairs verified by the membership program in the proposed header. + struct UcAndMembershipOutput { + UpdateClientOutput updateClientOutput; + IMembershipMsgs.KVPair[] kvPairs; + } +} diff --git a/contracts/light-clients/msgs/IUpdateClientMsgs.sol b/contracts/light-clients/msgs/IUpdateClientMsgs.sol new file mode 100644 index 000000000..fe120235 --- /dev/null +++ b/contracts/light-clients/msgs/IUpdateClientMsgs.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import { IICS07TendermintMsgs } from "./IICS07TendermintMsgs.sol"; + +/// @title Update Client Program Messages +/// @author srdtrk +/// @notice Defines shared types for the update client program. +interface IUpdateClientMsgs is IICS07TendermintMsgs { + /// @notice The message that is submitted to the updateClient function. + /// @param sp1Proof The SP1 proof for updating the client. + struct MsgUpdateClient { + SP1Proof sp1Proof; + } + + /// @notice The public value output for the sp1 update client program. + /// @param clientState The client state that was used to verify the header. + /// @param trustedConsensusState The trusted consensus state. + /// @param newConsensusState The new consensus state with the verified header. + /// @param time The time which the header was verified in seconds. + /// @param trustedHeight The trusted height. + /// @param newHeight The new height. + struct UpdateClientOutput { + ClientState clientState; + ConsensusState trustedConsensusState; + ConsensusState newConsensusState; + uint64 time; + Height trustedHeight; + Height newHeight; + } +} diff --git a/contracts/light-clients/utils/Paths.sol b/contracts/light-clients/utils/Paths.sol new file mode 100644 index 000000000..132e2a4f --- /dev/null +++ b/contracts/light-clients/utils/Paths.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +library Paths { + /// @notice Compares two bytes arrays + /// @param a The first bytes array + /// @param b The second bytes array + /// @return True if the two bytes arrays are equal, false otherwise + function equal(bytes[] memory a, bytes[] memory b) internal pure returns (bool) { + if (a.length != b.length) { + return false; + } + for (uint256 i = 0; i < a.length; i++) { + if (a[i].length != b[i].length) { + return false; + } + if (keccak256(a[i]) != keccak256(b[i])) { + return false; + } + } + return true; + } +} diff --git a/e2e/interchaintestv8/cosmos/utils.go b/e2e/interchaintestv8/cosmos/utils.go new file mode 100644 index 000000000..3f8b9103 --- /dev/null +++ b/e2e/interchaintestv8/cosmos/utils.go @@ -0,0 +1,23 @@ +package cosmos + +import ( + "cosmossdk.io/collections" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// CloneAppend returns a new slice with the contents of the provided slices. +func CloneAppend(bz []byte, tail []byte) (res []byte) { + res = make([]byte, len(bz)+len(tail)) + copy(res, bz) + copy(res[len(bz):], tail) + return +} + +// BankBalanceKey returns the store key for a given address and denomination pair. +func BankBalanceKey(addr sdk.AccAddress, denom string) ([]byte, error) { + keyCodec := collections.PairKeyCodec(sdk.AccAddressKey, collections.StringKey) + cKey := collections.Join(addr, denom) + return collections.EncodeKeyWithPrefix(banktypes.BalancesPrefix, keyCodec, cKey) +} diff --git a/e2e/interchaintestv8/e2esuite/utils.go b/e2e/interchaintestv8/e2esuite/utils.go index 7e2f85ae..ee641560 100644 --- a/e2e/interchaintestv8/e2esuite/utils.go +++ b/e2e/interchaintestv8/e2esuite/utils.go @@ -4,7 +4,9 @@ import ( "context" "crypto/ecdsa" "crypto/sha256" + "encoding/base64" "encoding/hex" + "encoding/json" "fmt" "io" "strconv" @@ -26,9 +28,17 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/tmhash" + cometproto "github.com/cometbft/cometbft/proto/tendermint/types" + comettypes "github.com/cometbft/cometbft/types" + comettime "github.com/cometbft/cometbft/types/time" + ibcwasmtypes "github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types" clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" ibcexported "github.com/cosmos/ibc-go/v9/modules/core/exported" + tmclient "github.com/cosmos/ibc-go/v9/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v9/testing" "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" @@ -295,3 +305,119 @@ func (s *TestSuite) GetUnionConsensusState(ctx context.Context, cosmosChain *cos return wasmConsenusState, ethConsensusState } + +func (s *TestSuite) CreateTMClientHeader( + ctx context.Context, + chain *cosmos.CosmosChain, + blockHeight int64, + timestamp time.Time, + oldHeader tmclient.Header, +) tmclient.Header { + var privVals []comettypes.PrivValidator + var validators []*comettypes.Validator + for _, chainVal := range chain.Validators { + keyBz, err := chainVal.ReadFile(ctx, "config/priv_validator_key.json") + s.Require().NoError(err) + var privValidatorKeyFile cosmos.PrivValidatorKeyFile + err = json.Unmarshal(keyBz, &privValidatorKeyFile) + s.Require().NoError(err) + decodedKeyBz, err := base64.StdEncoding.DecodeString(privValidatorKeyFile.PrivKey.Value) + s.Require().NoError(err) + + privKey := ed25519.PrivKey(decodedKeyBz) + privVal := comettypes.NewMockPVWithParams(privKey, false, false) + privVals = append(privVals, privVal) + + pubKey, err := privVal.GetPubKey() + s.Require().NoError(err) + + val := comettypes.NewValidator(pubKey, oldHeader.ValidatorSet.Proposer.VotingPower) + validators = append(validators, val) + + } + + valSet := comettypes.NewValidatorSet(validators) + vsetHash := valSet.Hash() + + // Make sure all the signers are in the correct order as expected by the validator set + signers := make([]comettypes.PrivValidator, valSet.Size()) + for i := range signers { + _, val := valSet.GetByIndex(int32(i)) + + for _, pv := range privVals { + pk, err := pv.GetPubKey() + s.Require().NoError(err) + + if pk.Equals(val.PubKey) { + signers[i] = pv + break + } + } + + if signers[i] == nil { + s.Require().FailNow("could not find signer for validator") + } + } + + tmHeader := comettypes.Header{ + Version: oldHeader.Header.Version, + ChainID: oldHeader.Header.ChainID, + Height: blockHeight, + Time: timestamp, + LastBlockID: ibctesting.MakeBlockID(make([]byte, tmhash.Size), 10_000, make([]byte, tmhash.Size)), + LastCommitHash: oldHeader.Header.LastCommitHash, + DataHash: tmhash.Sum([]byte("data_hash")), + ValidatorsHash: vsetHash, + NextValidatorsHash: vsetHash, + ConsensusHash: tmhash.Sum([]byte("consensus_hash")), + AppHash: tmhash.Sum([]byte("app_hash")), + LastResultsHash: tmhash.Sum([]byte("last_results_hash")), + EvidenceHash: tmhash.Sum([]byte("evidence_hash")), + ProposerAddress: valSet.Proposer.Address, + } + + hhash := tmHeader.Hash() + blockID := ibctesting.MakeBlockID(hhash, oldHeader.Commit.BlockID.PartSetHeader.Total, tmhash.Sum([]byte("part_set"))) + voteSet := comettypes.NewVoteSet(oldHeader.Header.ChainID, blockHeight, 1, cometproto.PrecommitType, valSet) + + voteProto := &comettypes.Vote{ + ValidatorAddress: nil, + ValidatorIndex: -1, + Height: blockHeight, + Round: 1, + Timestamp: comettime.Now(), + Type: cometproto.PrecommitType, + BlockID: blockID, + } + + for i, sign := range signers { + pv, err := sign.GetPubKey() + s.Require().NoError(err) + addr := pv.Address() + vote := voteProto.Copy() + vote.ValidatorAddress = addr + vote.ValidatorIndex = int32(i) + _, err = comettypes.SignAndCheckVote(vote, sign, oldHeader.Header.ChainID, false) + s.Require().NoError(err) + added, err := voteSet.AddVote(vote) + s.Require().NoError(err) + s.Require().True(added) + } + extCommit := voteSet.MakeExtendedCommit(comettypes.DefaultABCIParams()) + commit := extCommit.ToCommit() + + signedHeader := &cometproto.SignedHeader{ + Header: tmHeader.ToProto(), + Commit: commit.ToProto(), + } + + valSetProto, err := valSet.ToProto() + s.Require().NoError(err) + + return tmclient.Header{ + SignedHeader: signedHeader, + ValidatorSet: valSetProto, + TrustedHeight: oldHeader.TrustedHeight, + TrustedValidators: oldHeader.TrustedValidators, + } +} diff --git a/e2e/interchaintestv8/ethereum/ethereum.go b/e2e/interchaintestv8/ethereum/ethereum.go index 78d6d0f2..c2eff028 100644 --- a/e2e/interchaintestv8/ethereum/ethereum.go +++ b/e2e/interchaintestv8/ethereum/ethereum.go @@ -54,13 +54,18 @@ func NewEthereum(ctx context.Context, rpc string, beaconAPIClient *BeaconAPIClie }, nil } -func (e Ethereum) ForgeScript(deployer *ecdsa.PrivateKey, solidityContract string) ([]byte, error) { - cmd := exec.Command("forge", "script", "--rpc-url", e.RPC, "--broadcast", "--non-interactive", "-vvvv", solidityContract) +func (e Ethereum) ForgeScript(deployer *ecdsa.PrivateKey, solidityContract string, args ...string) ([]byte, error) { + args = append(args, "script", "--rpc-url", e.RPC, "--private-key", + hex.EncodeToString(deployer.D.Bytes()), "--broadcast", + "--non-interactive", "-vvvv", solidityContract, + ) + cmd := exec.Command( + "forge", args..., + ) faucetAddress := crypto.PubkeyToAddress(e.Faucet.PublicKey) extraEnv := []string{ fmt.Sprintf("%s=%s", testvalues.EnvKeyE2EFacuetAddress, faucetAddress.Hex()), - fmt.Sprintf("PRIVATE_KEY=0x%s", hex.EncodeToString(deployer.D.Bytes())), } cmd.Env = os.Environ() diff --git a/e2e/interchaintestv8/ethereum/utils.go b/e2e/interchaintestv8/ethereum/utils.go index f5dc61c4..7637ce52 100644 --- a/e2e/interchaintestv8/ethereum/utils.go +++ b/e2e/interchaintestv8/ethereum/utils.go @@ -68,6 +68,19 @@ func GetEthContractsFromDeployOutput(stdout string) (DeployedContracts, error) { return embeddedContracts, nil } +func GetOnlySp1Ics07AddressFromStdout(stdout string) (string, error) { + // Define the regular expression pattern + re := regexp.MustCompile(`"value":"(0x[0-9a-fA-F]+)"`) + + // Find the first match + matches := re.FindStringSubmatch(stdout) + if len(matches) <= 1 { + return "", fmt.Errorf("no matches found in stdout") + } + // Extract the value + return matches[1], nil +} + // From https://medium.com/@zhuytt4/verify-the-owner-of-safe-wallet-with-eth-getproof-7edc450504ff func GetCommitmentsStorageKey(path []byte) ethcommon.Hash { commitmentStorageSlot := ethcommon.FromHex(testvalues.IbcCommitmentSlotHex) diff --git a/e2e/interchaintestv8/ibc_eureka_test.go b/e2e/interchaintestv8/ibc_eureka_test.go index 261d28ab..77d13060 100644 --- a/e2e/interchaintestv8/ibc_eureka_test.go +++ b/e2e/interchaintestv8/ibc_eureka_test.go @@ -126,7 +126,7 @@ func (s *IbcEurekaTestSuite) SetupSuite(ctx context.Context, proofType operator. "--trust-level", testvalues.DefaultTrustLevel.String(), "--trusting-period", strconv.Itoa(testvalues.DefaultTrustPeriod), "-o", testvalues.Sp1GenesisFilePath, - }, proofType.ToOpGenesisArgs()...) + }, proofType.ToOperatorArgs()...) s.Require().NoError(operator.RunGenesis(args...)) var ( @@ -140,7 +140,7 @@ func (s *IbcEurekaTestSuite) SetupSuite(ctx context.Context, proofType operator. // make sure that the SP1_PRIVATE_KEY is set. s.Require().NotEmpty(os.Getenv(testvalues.EnvKeySp1PrivateKey)) - stdout, err = eth.ForgeScript(s.deployer, "scripts/E2ETestDeploy.s.sol:E2ETestDeploy") + stdout, err = eth.ForgeScript(s.deployer, testvalues.E2EDeployScriptPath) s.Require().NoError(err) default: s.Require().Fail("invalid prover type: %s", prover) @@ -1284,5 +1284,9 @@ func (s *IbcEurekaTestSuite) updateClientAndMembershipProof( uint64(trustedHeight), uint64(latestHeight), proofPathsStr, args..., ) s.Require().NoError(err) - return proofHeight, ucAndMemProof + + return &ics26router.IICS02ClientMsgsHeight{ + RevisionNumber: proofHeight.RevisionNumber, + RevisionHeight: proofHeight.RevisionHeight, + }, ucAndMemProof } diff --git a/e2e/interchaintestv8/operator/operator.go b/e2e/interchaintestv8/operator/operator.go index d9373f68..ae35779d 100644 --- a/e2e/interchaintestv8/operator/operator.go +++ b/e2e/interchaintestv8/operator/operator.go @@ -13,62 +13,146 @@ import ( "strconv" "strings" - "github.com/cosmos/solidity-ibc-eureka/abigen/ics26router" + abi "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/cosmos/cosmos-sdk/codec" + + tmclient "github.com/cosmos/ibc-go/v9/modules/light-clients/07-tendermint" + + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/testvalues" + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/types/sp1ics07tendermint" ) +type GenesisFixture struct { + TrustedClientState string `json:"trustedClientState"` + TrustedConsensusState string `json:"trustedConsensusState"` + UpdateClientVkey string `json:"updateClientVkey"` + MembershipVkey string `json:"membershipVkey"` + UcAndMembershipVkey string `json:"ucAndMembershipVkey"` + MisbehaviourVKey string `json:"misbehaviourVkey"` +} + // membershipFixture is a struct that contains the membership proof and proof height type membershipFixture struct { + GenesisFixture // hex encoded height ProofHeight string `json:"proofHeight"` // hex encoded proof MembershipProof string `json:"membershipProof"` } -func BinaryPath() string { +type misbehaviourFixture struct { + GenesisFixture + SubmitMsg string `json:"submitMsg"` +} + +// binaryPath is a function that returns the path to the operator binary +func binaryPath() string { return "operator" } // RunGenesis is a function that runs the genesis script to generate genesis.json func RunGenesis(args ...string) error { args = append([]string{"genesis"}, args...) - // nolint:gosec - cmd := exec.Command(BinaryPath(), args...) + cmd := exec.Command(binaryPath(), args...) cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr return cmd.Run() } // StartOperator is a function that runs the operator func StartOperator(args ...string) error { args = append([]string{"start"}, args...) - // nolint:gosec - cmd := exec.Command(BinaryPath(), args...) + cmd := exec.Command(binaryPath(), args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() } +// MembershipProof is a function that generates a membership proof and returns the proof height and proof +func MembershipProof(trusted_height uint64, paths string, writeFixtureName string, args ...string) (*sp1ics07tendermint.IICS02ClientMsgsHeight, []byte, error) { + args = append([]string{"fixtures", "membership", "--trusted-block", strconv.FormatUint(trusted_height, 10), "--key-paths", paths}, args...) + + cmd := exec.Command(binaryPath(), args...) + output, err := execOperatorCommand(cmd) + if err != nil { + return nil, nil, err + } + + // eliminate non-json characters + jsonStartIdx := strings.Index(string(output), "{") + if jsonStartIdx == -1 { + panic("no json found in output") + } + output = output[jsonStartIdx:] + + if writeFixtureName != "" { + fixtureFileName := fmt.Sprintf("%s/%s_fixture.json", testvalues.SP1ICS07FixturesDir, writeFixtureName) + if err := os.WriteFile(fixtureFileName, output, 0o600); err != nil { + return nil, nil, err + } + } + + var membership membershipFixture + err = json.Unmarshal(output, &membership) + if err != nil { + return nil, nil, err + } + + heightBz, err := hex.DecodeString(membership.ProofHeight) + if err != nil { + return nil, nil, err + } + + heightType, err := abi.NewType("tuple", "IICS02ClientMsgsHeight", []abi.ArgumentMarshaling{ + {Name: "revisionNumber", Type: "uint32"}, + {Name: "revisionHeight", Type: "uint32"}, + }) + if err != nil { + return nil, nil, err + } + + heightArgs := abi.Arguments{ + {Type: heightType, Name: "param_one"}, + } + + // abi encoding + heightI, err := heightArgs.Unpack(heightBz) + if err != nil { + return nil, nil, err + } + + height := abi.ConvertType(heightI[0], new(sp1ics07tendermint.IICS02ClientMsgsHeight)).(*sp1ics07tendermint.IICS02ClientMsgsHeight) + + if height.RevisionHeight != uint32(trusted_height) { + return nil, nil, errors.New("heights do not match") + } + + proofBz, err := hex.DecodeString(membership.MembershipProof) + if err != nil { + return nil, nil, err + } + + return height, proofBz, nil +} + // UpdateClientAndMembershipProof is a function that generates an update client and membership proof -func UpdateClientAndMembershipProof(trusted_height, target_height uint64, paths string, args ...string) (*ics26router.IICS02ClientMsgsHeight, []byte, error) { +func UpdateClientAndMembershipProof(trusted_height, target_height uint64, paths string, args ...string) (*sp1ics07tendermint.IICS02ClientMsgsHeight, []byte, error) { args = append([]string{"fixtures", "update-client-and-membership", "--trusted-block", strconv.FormatUint(trusted_height, 10), "--target-block", strconv.FormatUint(target_height, 10), "--key-paths", paths}, args...) - // nolint:gosec - cmd := exec.Command(BinaryPath(), args...) - stdout, err := execOperatorCommand(cmd) + + output, err := execOperatorCommand(exec.Command(binaryPath(), args...)) if err != nil { return nil, nil, err } // eliminate non-json characters - jsonStartIdx := strings.Index(string(stdout), "{") + jsonStartIdx := strings.Index(string(output), "{") if jsonStartIdx == -1 { panic("no json found in output") } - stdout = stdout[jsonStartIdx:] + output = output[jsonStartIdx:] var membership membershipFixture - err = json.Unmarshal(stdout, &membership) + err = json.Unmarshal(output, &membership) if err != nil { return nil, nil, err } @@ -96,7 +180,7 @@ func UpdateClientAndMembershipProof(trusted_height, target_height uint64, paths return nil, nil, err } - height := abi.ConvertType(heightI[0], new(ics26router.IICS02ClientMsgsHeight)).(*ics26router.IICS02ClientMsgsHeight) + height := abi.ConvertType(heightI[0], new(sp1ics07tendermint.IICS02ClientMsgsHeight)).(*sp1ics07tendermint.IICS02ClientMsgsHeight) if height.RevisionHeight != uint32(target_height) { return nil, nil, errors.New("heights do not match") @@ -110,22 +194,52 @@ func UpdateClientAndMembershipProof(trusted_height, target_height uint64, paths return height, proofBz, nil } -func execOperatorCommand(c *exec.Cmd) ([]byte, error) { - var outBuf bytes.Buffer +// MisbehaviourProof is a function that generates a misbehaviour proof and returns the submit message +func MisbehaviourProof(cdc codec.Codec, misbehaviour tmclient.Misbehaviour, writeFixtureName string, args ...string) ([]byte, error) { + misbehaviourBz, err := marshalMisbehaviour(cdc, misbehaviour) + if err != nil { + return nil, err + } - // Create a MultiWriter to write to both os.Stdout and the buffer - multiWriter := io.MultiWriter(os.Stdout, &outBuf) + // write misbehaviour to file for the operator to use + misbehaviourFileName := "misbehaviour.json" + if err := os.WriteFile(misbehaviourFileName, misbehaviourBz, 0o600); err != nil { + return nil, err + } + defer os.Remove(misbehaviourFileName) - // Set the command's stdout and stderror to the MultiWriter - c.Stdout = multiWriter - c.Stderr = multiWriter + args = append([]string{"fixtures", "misbehaviour", "--misbehaviour-path", misbehaviourFileName}, args...) + output, err := execOperatorCommand(exec.Command(binaryPath(), args...)) + if err != nil { + return nil, err + } - // Run the command - if err := c.Run(); err != nil { - return nil, fmt.Errorf("operator command '%s' failed: %s", strings.Join(c.Args, " "), outBuf.String()) + // eliminate non-json characters + jsonStartIdx := strings.Index(string(output), "{") + if jsonStartIdx == -1 { + panic("no json found in output") } + output = output[jsonStartIdx:] - return outBuf.Bytes(), nil + var misbehaviourFixture misbehaviourFixture + err = json.Unmarshal(output, &misbehaviourFixture) + if err != nil { + return nil, err + } + + if writeFixtureName != "" { + fixtureFileName := fmt.Sprintf("%s/misbehaviour_%s_fixture.json", testvalues.SP1ICS07FixturesDir, writeFixtureName) + if err := os.WriteFile(fixtureFileName, output, 0o600); err != nil { + return nil, err + } + } + + submitMsgBz, err := hex.DecodeString(misbehaviourFixture.SubmitMsg) + if err != nil { + return nil, err + } + + return submitMsgBz, nil } // ToBase64KeyPaths is a function that takes a list of key paths and returns a base64 encoded string @@ -140,3 +254,149 @@ func ToBase64KeyPaths(paths ...[][]byte) string { } return strings.Join(keyPaths, ",") } + +// TODO: This is a mighty ugly piece of code. Hopefully there is a better way to do this. +// marshalMisbehaviour takes a MisbehaviourProof struct and marshals it into a JSON byte slice that can be unmarshalled by the operator. +// It first marshals to JSON directly, and then modifies all the incompatible types (mostly base64 encoded bytes) to be hex encoded. +// Ideally, we can update the types in the operator to be more compatible with the type we have here. +// It might be enough to get out a new version of the rust crate "ibc-proto" and update the operator to use it. +func marshalMisbehaviour(cdc codec.Codec, misbehaviour tmclient.Misbehaviour) ([]byte, error) { + misbehaviour.ClientId = "07-tendermint-0" // We just have to set it to something to make the unmarshalling to work :P + bzIntermediary, err := cdc.MarshalJSON(&misbehaviour) + if err != nil { + return nil, err + } + var jsonIntermediary map[string]interface{} + if err := json.Unmarshal(bzIntermediary, &jsonIntermediary); err != nil { + return nil, err + } + headerHexPaths := []string{ + "validator_set.proposer.address", + "trusted_validators.proposer.address", + "signed_header.header.last_block_id.hash", + "signed_header.header.last_block_id.part_set_header.hash", + "signed_header.header.app_hash", + "signed_header.header.consensus_hash", + "signed_header.header.data_hash", + "signed_header.header.evidence_hash", + "signed_header.header.last_commit_hash", + "signed_header.header.last_results_hash", + "signed_header.header.next_validators_hash", + "signed_header.header.proposer_address", + "signed_header.header.validators_hash", + "signed_header.commit.block_id.hash", + "signed_header.commit.block_id.part_set_header.hash", + } + + var hexPaths []string + for _, path := range headerHexPaths { + hexPaths = append(hexPaths, "header_1."+path) + hexPaths = append(hexPaths, "header_2."+path) + } + + for _, path := range hexPaths { + pathParts := strings.Split(path, ".") + tmpIntermediary := jsonIntermediary + for i := 0; i < len(pathParts)-1; i++ { + var ok bool + tmpIntermediary, ok = tmpIntermediary[pathParts[i]].(map[string]interface{}) + if !ok { + fmt.Printf("path not found: %s\n", path) + continue + } + } + base64str, ok := tmpIntermediary[pathParts[len(pathParts)-1]].(string) + if !ok { + return nil, fmt.Errorf("path not found: %s", path) + } + bz, err := base64.StdEncoding.DecodeString(base64str) + if err != nil { + return nil, err + } + tmpIntermediary[pathParts[len(pathParts)-1]] = hex.EncodeToString(bz) + } + + validators1 := jsonIntermediary["header_1"].(map[string]interface{})["validator_set"].(map[string]interface{})["validators"].([]interface{}) + validators2 := jsonIntermediary["header_2"].(map[string]interface{})["validator_set"].(map[string]interface{})["validators"].([]interface{}) + trustedValidators1 := jsonIntermediary["header_1"].(map[string]interface{})["trusted_validators"].(map[string]interface{})["validators"].([]interface{}) + trustedValidators2 := jsonIntermediary["header_2"].(map[string]interface{})["trusted_validators"].(map[string]interface{})["validators"].([]interface{}) + validators := validators1 + validators = append(validators, validators2...) + validators = append(validators, trustedValidators1...) + validators = append(validators, trustedValidators2...) + for _, val := range validators { + val := val.(map[string]interface{}) + valAddressBase64Str, ok := val["address"].(string) + if !ok { + return nil, fmt.Errorf("address not found in path: %s", val) + } + valAddressBz, err := base64.StdEncoding.DecodeString(valAddressBase64Str) + if err != nil { + return nil, err + } + val["address"] = hex.EncodeToString(valAddressBz) + + pubKey, ok := val["pub_key"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("pub_key not found in path: %s", val) + } + ed25519PubKey := pubKey["ed25519"].(string) + pubKey["type"] = "tendermint/PubKeyEd25519" + pubKey["value"] = ed25519PubKey + } + + var pubKeys []map[string]interface{} + pubKeys = append(pubKeys, jsonIntermediary["header_1"].(map[string]interface{})["validator_set"].(map[string]interface{})["proposer"].(map[string]interface{})["pub_key"].(map[string]interface{})) + pubKeys = append(pubKeys, jsonIntermediary["header_1"].(map[string]interface{})["trusted_validators"].(map[string]interface{})["proposer"].(map[string]interface{})["pub_key"].(map[string]interface{})) + pubKeys = append(pubKeys, jsonIntermediary["header_2"].(map[string]interface{})["validator_set"].(map[string]interface{})["proposer"].(map[string]interface{})["pub_key"].(map[string]interface{})) + pubKeys = append(pubKeys, jsonIntermediary["header_2"].(map[string]interface{})["trusted_validators"].(map[string]interface{})["proposer"].(map[string]interface{})["pub_key"].(map[string]interface{})) + + for _, proposerPubKey := range pubKeys { + ed25519PubKey := proposerPubKey["ed25519"].(string) + proposerPubKey["type"] = "tendermint/PubKeyEd25519" + proposerPubKey["value"] = ed25519PubKey + } + + header1Sigs := jsonIntermediary["header_1"].(map[string]interface{})["signed_header"].(map[string]interface{})["commit"].(map[string]interface{})["signatures"].([]interface{}) + header2Sigs := jsonIntermediary["header_2"].(map[string]interface{})["signed_header"].(map[string]interface{})["commit"].(map[string]interface{})["signatures"].([]interface{}) + sigs := header1Sigs + sigs = append(sigs, header2Sigs...) + for _, sig := range sigs { + sig := sig.(map[string]interface{}) + if sig["block_id_flag"] == "BLOCK_ID_FLAG_COMMIT" { + sig["block_id_flag"] = 2 + } else { + return nil, fmt.Errorf("unexpected block_id_flag: %s", sig["block_id_flag"]) + } + + valAddressBase64Str, ok := sig["validator_address"].(string) + if !ok { + return nil, fmt.Errorf("validator_address not found") + } + valAddressBz, err := base64.StdEncoding.DecodeString(valAddressBase64Str) + if err != nil { + return nil, err + } + sig["validator_address"] = hex.EncodeToString(valAddressBz) + } + + return json.Marshal(jsonIntermediary) +} + +func execOperatorCommand(c *exec.Cmd) ([]byte, error) { + var outBuf bytes.Buffer + + // Create a MultiWriter to write to both os.Stdout and the buffer + multiWriter := io.MultiWriter(os.Stdout, &outBuf) + + // Set the command's stdout and stderror to the MultiWriter + c.Stdout = multiWriter + c.Stderr = multiWriter + + // Run the command + if err := c.Run(); err != nil { + return nil, fmt.Errorf("operator command '%s' failed: %s", strings.Join(c.Args, " "), outBuf.String()) + } + + return outBuf.Bytes(), nil +} diff --git a/e2e/interchaintestv8/operator/proofs.go b/e2e/interchaintestv8/operator/proofs.go index 8c301991..be2129c1 100644 --- a/e2e/interchaintestv8/operator/proofs.go +++ b/e2e/interchaintestv8/operator/proofs.go @@ -8,27 +8,14 @@ const ( ProofTypeGroth16 SupportedProofType = iota // ProofTypePlonk represents the Plonk SP1 proof type. ProofTypePlonk - // ProofTypeUnion represents the Union ICS23 proof type for membership proofs. - ProofTypeUnion ) // String returns the string representation of the proof type. func (pt SupportedProofType) String() string { - return [...]string{"groth16", "plonk", "union"}[pt] + return [...]string{"groth16", "plonk"}[pt] } // ToOperatorArgs returns the proof type as arguments for the operator command. func (pt SupportedProofType) ToOperatorArgs() []string { return []string{"-p", pt.String()} } - -// ToOpGenesisArgs returns the proof type as arguments for the operator genesis command. -// Genesis doesn't support the union proof type. -func (pt SupportedProofType) ToOpGenesisArgs() []string { - switch pt { - case ProofTypeUnion: - return []string{} - default: - return []string{"-p", pt.String()} - } -} diff --git a/e2e/interchaintestv8/relayer/relayer.go b/e2e/interchaintestv8/relayer/relayer.go index d0473571..13c0ff18 100644 --- a/e2e/interchaintestv8/relayer/relayer.go +++ b/e2e/interchaintestv8/relayer/relayer.go @@ -11,12 +11,14 @@ import ( relayertypes "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/types/relayer" ) -func BinaryPath() string { +// binaryPath returns the path to the relayer binary. +func binaryPath() string { return "relayer" } +// StartRelayer starts the relayer with the given config file. func StartRelayer(configPath string) (*os.Process, error) { - cmd := exec.Command(BinaryPath(), "start", "--config", configPath) + cmd := exec.Command(binaryPath(), "start", "--config", configPath) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -32,10 +34,12 @@ func StartRelayer(configPath string) (*os.Process, error) { return cmd.Process, nil } +// defaultGRPCAddress returns the default address for the gRPC server. func defaultGRPCAddress() string { return "127.0.0.1:3000" } +// GetGRPCClient returns a gRPC client for the relayer. func GetGRPCClient() (relayertypes.RelayerServiceClient, error) { conn, err := grpc.NewClient(defaultGRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { diff --git a/e2e/interchaintestv8/sp1_ics07_test.go b/e2e/interchaintestv8/sp1_ics07_test.go new file mode 100644 index 000000000..63408069 --- /dev/null +++ b/e2e/interchaintestv8/sp1_ics07_test.go @@ -0,0 +1,699 @@ +package main + +import ( + "context" + "crypto/ecdsa" + "crypto/rand" + "encoding/hex" + "fmt" + mathrand "math/rand" + "os" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/suite" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + + "cosmossdk.io/math" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + abci "github.com/cometbft/cometbft/abci/types" + + ibcclientutils "github.com/cosmos/ibc-go/v9/modules/core/02-client/client/utils" + clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" + ibchostv2 "github.com/cosmos/ibc-go/v9/modules/core/24-host/v2" + ibcexported "github.com/cosmos/ibc-go/v9/modules/core/exported" + tmclient "github.com/cosmos/ibc-go/v9/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v9/testing" + + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/cosmos" + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/e2esuite" + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/ethereum" + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/operator" + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/testvalues" + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/types/sp1ics07tendermint" +) + +// SP1ICS07TendermintTestSuite is a suite of tests that wraps TestSuite +// and can provide additional functionality +type SP1ICS07TendermintTestSuite struct { + e2esuite.TestSuite + + // Whether to generate fixtures for the solidity tests + generateFixtures bool + + // The private key of a test account + key *ecdsa.PrivateKey + // The SP1ICS07Tendermint contract + contract *sp1ics07tendermint.Contract +} + +// SetupSuite calls the underlying SP1ICS07TendermintTestSuite's SetupSuite method +// and deploys the SP1ICS07Tendermint contract +func (s *SP1ICS07TendermintTestSuite) SetupSuite(ctx context.Context, pt operator.SupportedProofType) { + s.TestSuite.SetupSuite(ctx) + + eth, simd := s.ChainA, s.ChainB + + s.Require().True(s.Run("Set up environment", func() { + err := os.Chdir("../..") + s.Require().NoError(err) + + s.key, err = eth.CreateAndFundUser() + s.Require().NoError(err) + + os.Setenv(testvalues.EnvKeyRustLog, testvalues.EnvValueRustLog_Info) + os.Setenv(testvalues.EnvKeyEthRPC, eth.RPC) + os.Setenv(testvalues.EnvKeyTendermintRPC, simd.GetHostRPCAddress()) + os.Setenv(testvalues.EnvKeySp1Prover, "network") + os.Setenv(testvalues.EnvKeyOperatorPrivateKey, hex.EncodeToString(crypto.FromECDSA(s.key))) + s.generateFixtures = os.Getenv(testvalues.EnvKeyGenerateFixtures) == testvalues.EnvValueGenerateFixtures_True + + // make sure that the SP1_PRIVATE_KEY is set. + s.Require().NotEmpty(os.Getenv(testvalues.EnvKeySp1PrivateKey)) + })) + + s.Require().True(s.Run("Deploy contracts", func() { + args := append([]string{ + "--trust-level", testvalues.DefaultTrustLevel.String(), + "--trusting-period", strconv.Itoa(testvalues.DefaultTrustPeriod), + "-o", testvalues.Sp1GenesisFilePath, + }, pt.ToOperatorArgs()...) + s.Require().NoError(operator.RunGenesis(args...)) + + s.T().Cleanup(func() { + err := os.Remove(testvalues.Sp1GenesisFilePath) + s.Require().NoError(err) + }) + + stdout, err := eth.ForgeScript(s.key, testvalues.SP1ICS07DeployScriptPath, "--json") + s.Require().NoError(err) + + contractAddress, err := ethereum.GetOnlySp1Ics07AddressFromStdout(string(stdout)) + s.Require().NoError(err) + s.Require().NotEmpty(contractAddress) + s.Require().True(ethcommon.IsHexAddress(contractAddress)) + + os.Setenv(testvalues.EnvKeyContractAddress, contractAddress) + + client, err := ethclient.Dial(eth.RPC) + s.Require().NoError(err) + + s.contract, err = sp1ics07tendermint.NewContract(ethcommon.HexToAddress(contractAddress), client) + s.Require().NoError(err) + })) +} + +// TestWithSP1ICS07TendermintTestSuite is the boilerplate code that allows the test suite to be run +func TestWithSP1ICS07TendermintTestSuite(t *testing.T) { + suite.Run(t, new(SP1ICS07TendermintTestSuite)) +} + +func (s *SP1ICS07TendermintTestSuite) TestDeploy_Groth16() { + ctx := context.Background() + s.DeployTest(ctx, operator.ProofTypeGroth16) +} + +func (s *SP1ICS07TendermintTestSuite) TestDeploy_Plonk() { + ctx := context.Background() + s.DeployTest(ctx, operator.ProofTypePlonk) +} + +// DeployTest tests the deployment of the SP1ICS07Tendermint contract with the given arguments +func (s *SP1ICS07TendermintTestSuite) DeployTest(ctx context.Context, pt operator.SupportedProofType) { + s.SetupSuite(ctx, pt) + + _, simd := s.ChainA, s.ChainB + + s.Require().True(s.Run("Verify deployment", func() { + clientState, err := s.contract.GetClientState(nil) + s.Require().NoError(err) + + stakingParams, err := simd.StakingQueryParams(ctx) + s.Require().NoError(err) + + s.Require().Equal(simd.Config().ChainID, clientState.ChainId) + s.Require().Equal(uint8(testvalues.DefaultTrustLevel.Numerator), clientState.TrustLevel.Numerator) + s.Require().Equal(uint8(testvalues.DefaultTrustLevel.Denominator), clientState.TrustLevel.Denominator) + s.Require().Equal(uint32(testvalues.DefaultTrustPeriod), clientState.TrustingPeriod) + s.Require().Equal(uint32(stakingParams.UnbondingTime.Seconds()), clientState.UnbondingPeriod) + s.Require().False(clientState.IsFrozen) + s.Require().Equal(uint32(1), clientState.LatestHeight.RevisionNumber) + s.Require().Greater(clientState.LatestHeight.RevisionHeight, uint32(0)) + })) +} + +func (s *SP1ICS07TendermintTestSuite) TestUpdateClient_Groth16() { + ctx := context.Background() + s.UpdateClientTest(ctx, operator.ProofTypeGroth16) +} + +func (s *SP1ICS07TendermintTestSuite) TestUpdateClient_Plonk() { + ctx := context.Background() + s.UpdateClientTest(ctx, operator.ProofTypePlonk) +} + +// UpdateClientTest tests the update client functionality +func (s *SP1ICS07TendermintTestSuite) UpdateClientTest(ctx context.Context, pt operator.SupportedProofType) { + s.SetupSuite(ctx, pt) + + _, simd := s.ChainA, s.ChainB + + if s.generateFixtures { + s.T().Log("Generate fixtures is set to true, but TestUpdateClient does not support it (yet)") + } + + s.Require().True(s.Run("Update client", func() { + clientState, err := s.contract.GetClientState(nil) + s.Require().NoError(err) + + initialHeight := clientState.LatestHeight.RevisionHeight + + s.Require().NoError(operator.StartOperator("--only-once")) // This should detect the proof type + + clientState, err = s.contract.GetClientState(nil) + s.Require().NoError(err) + + stakingParams, err := simd.StakingQueryParams(ctx) + s.Require().NoError(err) + + s.Require().Equal(simd.Config().ChainID, clientState.ChainId) + s.Require().Equal(uint8(testvalues.DefaultTrustLevel.Numerator), clientState.TrustLevel.Numerator) + s.Require().Equal(uint8(testvalues.DefaultTrustLevel.Denominator), clientState.TrustLevel.Denominator) + s.Require().Equal(uint32(testvalues.DefaultTrustPeriod), clientState.TrustingPeriod) + s.Require().Equal(uint32(stakingParams.UnbondingTime.Seconds()), clientState.UnbondingPeriod) + s.Require().False(clientState.IsFrozen) + s.Require().Equal(uint32(1), clientState.LatestHeight.RevisionNumber) + s.Require().Greater(clientState.LatestHeight.RevisionHeight, initialHeight) + })) +} + +// TestSP1Membership tests the verify (non)membership functionality with the plonk flag +func (s *SP1ICS07TendermintTestSuite) TestMembership_Plonk() { + s.MembershipTest(operator.ProofTypePlonk) +} + +// TestSP1Membership tests the verify (non)membership functionality with the plonk flag +func (s *SP1ICS07TendermintTestSuite) TestMembership_Groth16() { + s.MembershipTest(operator.ProofTypeGroth16) +} + +// MembershipTest tests the verify (non)membership functionality with the given arguments +func (s *SP1ICS07TendermintTestSuite) MembershipTest(pt operator.SupportedProofType) { + ctx := context.Background() + + s.SetupSuite(ctx, pt) + + eth, simd := s.ChainA, s.ChainB + + if s.generateFixtures { + s.T().Log("Generate fixtures is set to true, but TestVerifyMembership does not support it (yet)") + } + + s.Require().True(s.Run("Verify membership", func() { + var membershipKey [][]byte + s.Require().True(s.Run("Generate keys", func() { + // Prove the bank balance of UserA + key, err := cosmos.BankBalanceKey(s.UserB.Address(), simd.Config().Denom) + s.Require().NoError(err) + + membershipKey = [][]byte{[]byte(banktypes.StoreKey), key} + })) + + clientState, err := s.contract.GetClientState(nil) + s.Require().NoError(err) + + trustedHeight := clientState.LatestHeight.RevisionHeight + + var expValue []byte + s.Require().True(s.Run("Get expected value for the verify membership", func() { + resp, err := e2esuite.ABCIQuery(ctx, simd, &abci.RequestQuery{ + Path: "store/" + string(membershipKey[0]) + "/key", + Data: membershipKey[1], + Height: int64(trustedHeight) - 1, + }) + s.Require().NoError(err) + s.Require().NotEmpty(resp.Value) + + expValue = resp.Value + })) + + memArgs := append([]string{"--trust-level", testvalues.DefaultTrustLevel.String(), "--trusting-period", strconv.Itoa(testvalues.DefaultTrustPeriod), "--base64"}, pt.ToOperatorArgs()...) + proofHeight, ucAndMemProof, err := operator.MembershipProof( + uint64(trustedHeight), operator.ToBase64KeyPaths(membershipKey), "", + memArgs..., + ) + s.Require().NoError(err) + + msg := sp1ics07tendermint.ILightClientMsgsMsgMembership{ + ProofHeight: *proofHeight, + Proof: ucAndMemProof, + Path: membershipKey, + Value: expValue, + } + + tx, err := s.contract.Membership(s.GetTransactOpts(s.key), msg) + s.Require().NoError(err) + + // wait until transaction is included in a block + receipt := s.GetTxReciept(ctx, eth, tx.Hash()) + s.T().Logf("Gas used in %s: %d", s.T().Name(), receipt.GasUsed) + })) + + s.Require().True(s.Run("Verify non-membership", func() { + var nonMembershipKey [][]byte + s.Require().True(s.Run("Generate keys", func() { + // A non-membership key: + packetReceiptPath := ibchostv2.PacketReceiptKey(ibctesting.FirstChannelID, 1) + + nonMembershipKey = [][]byte{[]byte(ibcexported.StoreKey), packetReceiptPath} + })) + + clientState, err := s.contract.GetClientState(nil) + s.Require().NoError(err) + + trustedHeight := clientState.LatestHeight.RevisionHeight + + nonMemArgs := append([]string{"--trust-level", testvalues.DefaultTrustLevel.String(), "--trusting-period", strconv.Itoa(testvalues.DefaultTrustPeriod), "--base64"}, pt.ToOperatorArgs()...) + proofHeight, ucAndMemProof, err := operator.MembershipProof( + uint64(trustedHeight), operator.ToBase64KeyPaths(nonMembershipKey), "", + nonMemArgs..., + ) + s.Require().NoError(err) + + msg := sp1ics07tendermint.ILightClientMsgsMsgMembership{ + ProofHeight: *proofHeight, + Proof: ucAndMemProof, + Path: nonMembershipKey, + Value: []byte(""), + } + + tx, err := s.contract.Membership(s.GetTransactOpts(s.key), msg) + s.Require().NoError(err) + + // wait until transaction is included in a block + receipt := s.GetTxReciept(ctx, eth, tx.Hash()) + s.T().Logf("Gas used in %s: %d", s.T().Name(), receipt.GasUsed) + })) +} + +func (s *SP1ICS07TendermintTestSuite) TestUpdateClientAndMembership_Plonk() { + ctx := context.Background() + s.UpdateClientAndMembershipTest(ctx, operator.ProofTypePlonk) +} + +func (s *SP1ICS07TendermintTestSuite) TestUpdateClientAndMembership_Groth16() { + ctx := context.Background() + s.UpdateClientAndMembershipTest(ctx, operator.ProofTypeGroth16) +} + +// UpdateClientAndMembershipTest tests the update client and membership functionality with the given arguments +func (s *SP1ICS07TendermintTestSuite) UpdateClientAndMembershipTest(ctx context.Context, pt operator.SupportedProofType) { + s.SetupSuite(ctx, pt) + + eth, simd := s.ChainA, s.ChainB + + if s.generateFixtures { + s.T().Log("Generate fixtures is set to true, but TestUpdateClientAndMembership does not support it (yet)") + } + + s.Require().True(s.Run("Update and verify (non)membership", func() { + var ( + membershipKey [][]byte + nonMembershipKey [][]byte + ) + s.Require().True(s.Run("Generate keys", func() { + // Prove the bank balance of UserA + key, err := cosmos.BankBalanceKey(s.UserB.Address(), simd.Config().Denom) + s.Require().NoError(err) + + membershipKey = [][]byte{[]byte(banktypes.StoreKey), key} + + // A non-membership key: + packetReceiptPath := ibchostv2.PacketReceiptKey(ibctesting.FirstChannelID, 1) + + nonMembershipKey = [][]byte{[]byte(ibcexported.StoreKey), packetReceiptPath} + })) + + clientState, err := s.contract.GetClientState(nil) + s.Require().NoError(err) + + trustedHeight := clientState.LatestHeight.RevisionHeight + + latestHeight, err := simd.Height(ctx) + s.Require().NoError(err) + + s.Require().Greater(uint32(latestHeight), trustedHeight) + + var expValue []byte + s.Require().True(s.Run("Get expected value for the verify membership", func() { + resp, err := e2esuite.ABCIQuery(ctx, simd, &abci.RequestQuery{ + Path: "store/" + string(membershipKey[0]) + "/key", + Data: membershipKey[1], + Height: latestHeight - 1, + }) + s.Require().NoError(err) + s.Require().NotEmpty(resp.Value) + + expValue = resp.Value + })) + + args := append([]string{"--trust-level", testvalues.DefaultTrustLevel.String(), "--trusting-period", strconv.Itoa(testvalues.DefaultTrustPeriod), "--base64"}, pt.ToOperatorArgs()...) + proofHeight, ucAndMemProof, err := operator.UpdateClientAndMembershipProof( + uint64(trustedHeight), uint64(latestHeight), + operator.ToBase64KeyPaths(membershipKey, nonMembershipKey), + args..., + ) + s.Require().NoError(err) + + msg := sp1ics07tendermint.ILightClientMsgsMsgMembership{ + ProofHeight: *proofHeight, + Proof: ucAndMemProof, + Path: membershipKey, + Value: expValue, + } + + tx, err := s.contract.Membership(s.GetTransactOpts(s.key), msg) + s.Require().NoError(err) + + // wait until transaction is included in a block + receipt := s.GetTxReciept(ctx, eth, tx.Hash()) + s.T().Logf("Gas used in %s: %d", s.T().Name(), receipt.GasUsed) + + clientState, err = s.contract.GetClientState(nil) + s.Require().NoError(err) + + s.Require().Equal(uint32(1), clientState.LatestHeight.RevisionNumber) + s.Require().Greater(clientState.LatestHeight.RevisionHeight, trustedHeight) + s.Require().Equal(proofHeight.RevisionHeight, clientState.LatestHeight.RevisionHeight) + s.Require().False(clientState.IsFrozen) + })) +} + +func (s *SP1ICS07TendermintTestSuite) TestDoubleSignMisbehaviour_Plonk() { + ctx := context.Background() + s.DoubleSignMisbehaviourTest(ctx, "double_sign-plonk", operator.ProofTypePlonk) +} + +func (s *SP1ICS07TendermintTestSuite) TestDoubleSignMisbehaviour_Groth16() { + ctx := context.Background() + s.DoubleSignMisbehaviourTest(ctx, "double_sign-groth16", operator.ProofTypeGroth16) +} + +// DoubleSignMisbehaviourTest tests the misbehaviour functionality with the given arguments +// Fixture is only generated if the environment variable is set +// Partially based on https://github.com/cosmos/relayer/blob/f9aaf3dd0ebfe99fbe98d190a145861d7df93804/interchaintest/misbehaviour_test.go#L38 +func (s *SP1ICS07TendermintTestSuite) DoubleSignMisbehaviourTest(ctx context.Context, fixName string, pt operator.SupportedProofType) { + s.SetupSuite(ctx, pt) + + eth, simd := s.ChainA, s.ChainB + _ = eth + + var height clienttypes.Height + var trustedHeader tmclient.Header + s.Require().True(s.Run("Get trusted header", func() { + var latestHeight int64 + var err error + trustedHeader, latestHeight, err = ibcclientutils.QueryTendermintHeader(simd.Validators[0].CliContext()) + s.Require().NoError(err) + s.Require().NotZero(latestHeight) + + height = clienttypes.NewHeight(clienttypes.ParseChainID(simd.Config().ChainID), uint64(latestHeight)) + + clientState, err := s.contract.GetClientState(nil) + s.Require().NoError(err) + trustedHeight := clienttypes.NewHeight(uint64(clientState.LatestHeight.RevisionNumber), uint64(clientState.LatestHeight.RevisionHeight)) + + trustedHeader.TrustedHeight = trustedHeight + trustedHeader.TrustedValidators = trustedHeader.ValidatorSet + })) + + s.Require().True(s.Run("Invalid misbehaviour", func() { + // Create a new valid header + newHeader := s.CreateTMClientHeader( + ctx, + simd, + int64(height.RevisionHeight+1), + trustedHeader.GetTime().Add(time.Minute), + trustedHeader, + ) + + invalidMisbehaviour := tmclient.Misbehaviour{ + Header1: &newHeader, + Header2: &trustedHeader, + } + + // The proof should fail because this is not misbehaviour (valid header for a new block) + args := append([]string{ + "--trust-level", testvalues.DefaultTrustLevel.String(), + "--trusting-period", strconv.Itoa(testvalues.DefaultTrustPeriod), + }, + pt.ToOperatorArgs()..., + ) + _, err := operator.MisbehaviourProof(simd.GetCodec(), invalidMisbehaviour, "", args...) + s.Require().ErrorContains(err, "Misbehaviour is not detected") + })) + + s.Require().True(s.Run("Valid misbehaviour", func() { + // create a duplicate header (with a different hash) + newHeader := s.CreateTMClientHeader( + ctx, + simd, + int64(height.RevisionHeight), + trustedHeader.GetTime().Add(time.Minute), + trustedHeader, + ) + + misbehaviour := tmclient.Misbehaviour{ + Header1: &newHeader, + Header2: &trustedHeader, + } + + var fixtureName string + if s.generateFixtures { + fixtureName = fixName + } + args := append([]string{ + "--trust-level", testvalues.DefaultTrustLevel.String(), + "--trusting-period", strconv.Itoa(testvalues.DefaultTrustPeriod), + }, + pt.ToOperatorArgs()..., + ) + submitMsg, err := operator.MisbehaviourProof(simd.GetCodec(), misbehaviour, fixtureName, args...) + s.Require().NoError(err) + + tx, err := s.contract.Misbehaviour(s.GetTransactOpts(s.key), submitMsg) + s.Require().NoError(err) + + // wait until transaction is included in a block + receipt := s.GetTxReciept(ctx, eth, tx.Hash()) + s.T().Logf("Gas used in %s: %d", s.T().Name(), receipt.GasUsed) + + clientState, err := s.contract.GetClientState(nil) + s.Require().NoError(err) + s.Require().True(clientState.IsFrozen) + })) +} + +func (s *SP1ICS07TendermintTestSuite) TestBreakingTimeMonotonicityMisbehaviour_Plonk() { + ctx := context.Background() + s.BreakingTimeMonotonicityMisbehaviourTest(ctx, "breaking_time_monotonicity-plonk", operator.ProofTypePlonk) +} + +func (s *SP1ICS07TendermintTestSuite) TestBreakingTimeMonotonicityMisbehaviour_Groth16() { + ctx := context.Background() + s.BreakingTimeMonotonicityMisbehaviourTest(ctx, "breaking_time_monotonicity-groth16", operator.ProofTypeGroth16) +} + +// TestBreakingTimeMonotonicityMisbehaviour tests the misbehaviour functionality +// Fixture is only generated if the environment variable is set +// Partially based on https://github.com/cosmos/relayer/blob/f9aaf3dd0ebfe99fbe98d190a145861d7df93804/interchaintest/misbehaviour_test.go#L38 +func (s *SP1ICS07TendermintTestSuite) BreakingTimeMonotonicityMisbehaviourTest(ctx context.Context, fixName string, pt operator.SupportedProofType) { + s.SetupSuite(ctx, pt) + + eth, simd := s.ChainA, s.ChainB + + var height clienttypes.Height + var trustedHeader tmclient.Header + s.Require().True(s.Run("Get trusted header", func() { + var latestHeight int64 + var err error + trustedHeader, latestHeight, err = ibcclientutils.QueryTendermintHeader(simd.Validators[0].CliContext()) + s.Require().NoError(err) + s.Require().NotZero(latestHeight) + + height = clienttypes.NewHeight(clienttypes.ParseChainID(simd.Config().ChainID), uint64(latestHeight)) + + clientState, err := s.contract.GetClientState(nil) + s.Require().NoError(err) + trustedHeight := clienttypes.NewHeight(uint64(clientState.LatestHeight.RevisionNumber), uint64(clientState.LatestHeight.RevisionHeight)) + + trustedHeader.TrustedHeight = trustedHeight + trustedHeader.TrustedValidators = trustedHeader.ValidatorSet + })) + + s.Require().True(s.Run("Valid misbehaviour", func() { + // we have a trusted height n from trustedHeader + // we now create two new headers n+1 and n+2 where both have time later than n + // but n+2 has time earlier than n+1, which breaks time monotonicity + + // n+1 + header2 := s.CreateTMClientHeader( + ctx, + simd, + int64(height.RevisionHeight+1), + trustedHeader.GetTime().Add(time.Minute), + trustedHeader, + ) + + // n+2 (with time earlier than n+1 and still after n) + header1 := s.CreateTMClientHeader( + ctx, + simd, + int64(height.RevisionHeight+2), + trustedHeader.GetTime().Add(time.Minute).Add(-30*time.Second), + trustedHeader, + ) + + misbehaviour := tmclient.Misbehaviour{ + Header1: &header1, + Header2: &header2, + } + + var fixtureName string + if s.generateFixtures { + fixtureName = fixName + } + args := append([]string{ + "--trust-level", testvalues.DefaultTrustLevel.String(), + "--trusting-period", strconv.Itoa(testvalues.DefaultTrustPeriod), + }, + pt.ToOperatorArgs()..., + ) + submitMsg, err := operator.MisbehaviourProof(simd.GetCodec(), misbehaviour, fixtureName, args...) + s.Require().NoError(err) + + tx, err := s.contract.Misbehaviour(s.GetTransactOpts(s.key), submitMsg) + s.Require().NoError(err) + + // wait until transaction is included in a block + receipt := s.GetTxReciept(ctx, eth, tx.Hash()) + s.T().Logf("Gas used in %s: %d", s.T().Name(), receipt.GasUsed) + + clientState, err := s.contract.GetClientState(nil) + s.Require().NoError(err) + s.Require().True(clientState.IsFrozen) + })) +} + +func (s *SP1ICS07TendermintTestSuite) Test100Membership_Groth16() { + s.largeMembershipTest(100, operator.ProofTypeGroth16) +} + +func (s *SP1ICS07TendermintTestSuite) Test25Membership_Plonk() { + s.largeMembershipTest(25, operator.ProofTypePlonk) +} + +// largeMembershipTest tests membership proofs with a large number of key-value pairs +func (s *SP1ICS07TendermintTestSuite) largeMembershipTest(n uint64, pt operator.SupportedProofType) { + ctx := context.Background() + + s.SetupSuite(ctx, pt) + + eth, simd := s.ChainA, s.ChainB + + s.Require().True(s.Run(fmt.Sprintf("Large membership test with %d key-value pairs", n), func() { + membershipKeys := make([][][]byte, n) + s.Require().True(s.Run("Generate state and keys", func() { + // Messages to generate state to be used in the membership proof + msgs := []sdk.Msg{} + // Generate a random addresses + pubBz := make([]byte, ed25519.PubKeySize) + pub := &ed25519.PubKey{Key: pubBz} + for i := uint64(0); i < n; i++ { + _, err := rand.Read(pubBz) + s.Require().NoError(err) + acc := sdk.AccAddress(pub.Address()) + + // Send some funds to the address + msgs = append(msgs, banktypes.NewMsgSend(s.UserB.Address(), acc, sdk.NewCoins(sdk.NewCoin(simd.Config().Denom, math.NewInt(1))))) + + key, err := cosmos.BankBalanceKey(s.UserB.Address(), simd.Config().Denom) + s.Require().NoError(err) + + membershipKeys[i] = [][]byte{[]byte(banktypes.StoreKey), key} + } + + // Send the messages + _, err := s.BroadcastMessages(ctx, simd, s.UserB, 20_000_000, msgs...) + s.Require().NoError(err) + })) + + // update the client + clientHeight := s.UpdateClient(ctx) + + s.Require().True(s.Run("Verify membership", func() { + rndIdx := mathrand.Intn(int(n)) + + var expValue []byte + s.Require().True(s.Run("Get expected value for the verify membership", func() { + resp, err := e2esuite.ABCIQuery(ctx, simd, &abci.RequestQuery{ + Path: fmt.Sprintf("store/%s/key", membershipKeys[rndIdx][0]), + Data: membershipKeys[rndIdx][1], + Height: int64(clientHeight.RevisionHeight) - 1, + }) + s.Require().NoError(err) + s.Require().NotEmpty(resp.Value) + + expValue = resp.Value + })) + + var fixtureName string + if s.generateFixtures { + fixtureName = fmt.Sprintf("membership_%d-%s", n, pt.String()) + } + args := append([]string{"--trust-level", testvalues.DefaultTrustLevel.String(), "--trusting-period", strconv.Itoa(testvalues.DefaultTrustPeriod), "--base64"}, pt.ToOperatorArgs()...) + proofHeight, memProof, err := operator.MembershipProof( + clientHeight.RevisionHeight, operator.ToBase64KeyPaths(membershipKeys...), + fixtureName, args..., + ) + s.Require().NoError(err) + + msg := sp1ics07tendermint.ILightClientMsgsMsgMembership{ + ProofHeight: *proofHeight, + Proof: memProof, + Path: membershipKeys[rndIdx], + Value: expValue, + } + + tx, err := s.contract.Membership(s.GetTransactOpts(s.key), msg) + s.Require().NoError(err) + + // wait until transaction is included in a block + _ = s.GetTxReciept(ctx, eth, tx.Hash()) + })) + })) +} + +// UpdateClient updates the SP1ICS07Tendermint client and returns the new height +func (s *SP1ICS07TendermintTestSuite) UpdateClient(ctx context.Context) clienttypes.Height { + var updatedClientState sp1ics07tendermint.IICS07TendermintMsgsClientState + s.Require().True(s.Run("Update client", func() { + s.Require().NoError(operator.StartOperator("--only-once")) + var err error + updatedClientState, err = s.contract.GetClientState(nil) + s.Require().NoError(err) + })) + + return clienttypes.Height{ + RevisionNumber: uint64(updatedClientState.LatestHeight.RevisionNumber), + RevisionHeight: uint64(updatedClientState.LatestHeight.RevisionHeight), + } +} diff --git a/e2e/interchaintestv8/testvalues/values.go b/e2e/interchaintestv8/testvalues/values.go index d9956a14..ab31a71d 100644 --- a/e2e/interchaintestv8/testvalues/values.go +++ b/e2e/interchaintestv8/testvalues/values.go @@ -31,6 +31,8 @@ const ( EnvKeyGenerateFixtures = "GENERATE_FIXTURES" // The log level for the Rust logger. EnvKeyRustLog = "RUST_LOG" + // Address of the SP1ICS07Tendermint contract for operator. + EnvKeyContractAddress = "CONTRACT_ADDRESS" // Log level for the Rust logger. EnvValueRustLog_Info = "info" @@ -52,11 +54,17 @@ const ( // Sp1GenesisFilePath is the path to the genesis file for the SP1 chain. // This file is generated and then deleted by the test. - Sp1GenesisFilePath = "e2e/genesis.json" - // FixturesDir is the directory where the Solidity fixtures are stored. - FixturesDir = "test/fixtures/" + Sp1GenesisFilePath = "scripts/genesis.json" + // SolidityFixturesDir is the directory where the Solidity fixtures are stored. + SolidityFixturesDir = "test/solidity-ibc/fixtures/" + // SP1ICS07FixturesDir is the directory where the SP1ICS07 fixtures are stored. + SP1ICS07FixturesDir = "test/sp1-ics07/fixtures" // RelayerConfigFilePath is the path to generate the relayer config file. - RelayerConfigFilePath = "relayer/config.json" + RelayerConfigFilePath = "programs/relayer/config.json" + // E2EDeployScriptPath is the path to the E2E deploy script. + E2EDeployScriptPath = "scripts/E2ETestDeploy.s.sol:E2ETestDeploy" + // SP1ICS07DeployScriptPath is the path to the SP1ICS07 deploy script. + SP1ICS07DeployScriptPath = "scripts/SP1ICS07Tendermint.s.sol:SP1TendermintScript" // DefaultGovV1ProposalTokenAmount is the default amount of tokens used to submit a proposal. DefaultGovV1ProposalTokenAmount = 500_000_000 diff --git a/e2e/interchaintestv8/types/fixtures.go b/e2e/interchaintestv8/types/fixtures.go index 716feb91..609c133c 100644 --- a/e2e/interchaintestv8/types/fixtures.go +++ b/e2e/interchaintestv8/types/fixtures.go @@ -116,7 +116,7 @@ func GenerateAndSaveFixture(fileName, erc20Address, methodName string, msg any, return err } - filePath := testvalues.FixturesDir + fileName + filePath := testvalues.SolidityFixturesDir + "/" + fileName // nolint:gosec return os.WriteFile(filePath, fixtureBz, 0o644) } diff --git a/foundry.toml b/foundry.toml index 8a2d88aa..3fcd7fde 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,6 +8,7 @@ fuzz = { runs = 100_000 } gas_reports = ["*"] optimizer = true + via_ir = true # This is needed because of the `sp1-ics07-tendermint` contract optimizer_runs = 10_000 out = "out" script = "script" diff --git a/justfile b/justfile index 6d4204f4..948358ba 100644 --- a/justfile +++ b/justfile @@ -1,8 +1,5 @@ set dotenv-load -# Use the SP1_OPERATOR_REV environment variable if it is set, otherwise use a default commit hash -sp1_operator_rev := env_var_or_default('SP1_OPERATOR_REV', 'f67f5fec9423a4744092ee98b62bc60e3354f223') - # Build the contracts using `forge build` build-contracts: clean forge build @@ -11,6 +8,21 @@ build-contracts: clean build-relayer: cargo build --bin relayer --release --locked +# Build the operator using `cargo build` +build-operator: + cargo build --bin operator --release --locked + +# Build riscv elf files using `~/.sp1/bin/cargo-prove` +build-sp1-programs: + cd programs/sp1-programs/update-client && ~/.sp1/bin/cargo-prove prove build --elf-name update-client-riscv32im-succinct-zkvm-elf + @echo "ELF created at 'elf/update-client-riscv32im-succinct-zkvm-elf'" + cd programs/sp1-programs/membership && ~/.sp1/bin/cargo-prove prove build --elf-name membership-riscv32im-succinct-zkvm-elf + @echo "ELF created at 'elf/membership-riscv32im-succinct-zkvm-elf'" + cd programs/sp1-programs/uc-and-membership && ~/.sp1/bin/cargo-prove prove build --elf-name uc-and-membership-riscv32im-succinct-zkvm-elf + @echo "ELF created at 'elf/uc-and-membership-riscv32im-succinct-zkvm-elf'" + cd programs/sp1-programs/misbehaviour && ~/.sp1/bin/cargo-prove prove build --elf-name misbehaviour-riscv32im-succinct-zkvm-elf + @echo "ELF created at 'elf/misbehaviour-riscv32im-succinct-zkvm-elf'" + # Clean up the cache and out directories clean: @echo "Cleaning up cache and out directories" @@ -24,7 +36,7 @@ test-foundry testname=".\\*": # Run with `just test-benchmark Plonk"` to run only Plonk benchmarks # Run with `just test-benchmark Groth16"` to run only Groth16 benchmarks test-benchmark testname=".\\*": - forge test -vvv --show-progress --gas-report --match-path test/BenchmarkTest.t.sol --match-test {{testname}} + forge test -vvv --show-progress --gas-report --match-path test/solidity-ibc/BenchmarkTest.t.sol --match-test {{testname}} # Run the cargo tests test-cargo: @@ -38,7 +50,7 @@ test-abigen: # Run forge fmt and bun solhint lint: @echo "Linting the Solidity code..." - forge fmt --check && bun solhint -w 0 '{script,contracts,test}/**/*.sol' + forge fmt --check && bun solhint -w 0 '{scripts,contracts,test}/**/*.sol' @echo "Linting the Go code..." cd e2e/interchaintestv8 && golangci-lint run . @echo "Linting the Rust code..." @@ -74,16 +86,33 @@ test-e2e-relayer testname: clean @echo "Running {{testname}} test..." cd e2e/interchaintestv8 && go test -v -run '^TestWithRelayerTestSuite/{{testname}}$' -timeout 40m +# Run the e2e tests in the relayer test suite +test-e2e-sp1-ics07 testname: clean + @echo "Running {{testname}} test..." + cd e2e/interchaintestv8 && go test -v -run '^TestWithSP1ICS07TendermintTestSuite/{{testname}}$' -timeout 40m + # Install the sp1-ics07-tendermint operator for use in the e2e tests install-operator: - cargo install --git https://github.com/cosmos/sp1-ics07-tendermint --rev {{sp1_operator_rev}} sp1-ics07-tendermint-operator --bin operator --locked + cargo install --bin operator --path programs/operator --locked # Install the relayer using `cargo install` install-relayer: - cargo install --bin relayer --path relayer --locked + cargo install --bin relayer --path programs/relayer --locked + +# Generate the `genesis.json` file using $TENDERMINT_RPC_URL in the `.env` file +# Note that the `scripts/genesis.json` file is ignored in the `.gitignore` file +genesis-sp1-ics07: build-sp1-programs + @echo "Generating the genesis file..." + RUST_LOG=info cargo run --bin operator --release -- genesis -o scripts/genesis.json + +# Deploy the SP1ICS07Tendermint contract to the Eth Sepolia testnet if the `.env` file is present +deploy-sp1-ics07: genesis-sp1-ics07 + @echo "Deploying the SP1ICS07Tendermint contract" + forge install + forge script scripts/SP1ICS07Tendermint.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast # Generate the fixtures for the Solidity tests using the e2e tests -generate-fixtures: clean +generate-fixtures-solidity: clean @echo "Generating fixtures... This may take a while." @echo "Generating recvPacket and acknowledgePacket groth16 fixtures..." cd e2e/interchaintestv8 && GENERATE_FIXTURES=true SP1_PROVER=network go test -v -run '^TestWithIbcEurekaTestSuite/TestICS20TransferERC20TokenfromEthereumToCosmosAndBack_Groth16$' -timeout 40m @@ -104,6 +133,30 @@ generate-fixtures: clean @echo "Generating timeoutPacket plonk fixtures..." cd e2e/interchaintestv8 && GENERATE_FIXTURES=true SP1_PROVER=network go test -v -run '^TestWithIbcEurekaTestSuite/TestICS20TransferTimeoutFromEthereumToCosmosChain_Plonk$' -timeout 40m +# Generate the fixture files for the Celestia Mocha testnet using the prover parameter. +# The prover parameter should be one of: ["mock", "network", "local"] +# This generates the fixtures for all programs in parallel using GNU parallel. +# If prover is set to network, this command requires the `SP1_PRIVATE_KEY` environment variable to be set. +generate-fixtures-sp1-ics07: build-operator + @echo "Generating fixtures... This may take a while (up to 20 minutes)" + TENDERMINT_RPC_URL="${TENDERMINT_RPC_URL%/}" && \ + CURRENT_HEIGHT=$(curl "$TENDERMINT_RPC_URL"/block | jq -r ".result.block.header.height") && \ + TRUSTED_HEIGHT=$(($CURRENT_HEIGHT-100)) && \ + TARGET_HEIGHT=$(($CURRENT_HEIGHT-10)) && \ + echo "For celestia fixtures, trusted block: $TRUSTED_HEIGHT, target block: $TARGET_HEIGHT, from $TENDERMINT_RPC_URL" && \ + parallel --progress --shebang --ungroup -j 6 ::: \ + "RUST_LOG=info SP1_PROVER=network ./target/release/operator fixtures update-client --trusted-block $TRUSTED_HEIGHT --target-block $TARGET_HEIGHT -o 'test/sp1-ics07/fixtures/update_client_fixture-plonk.json'" \ + "sleep 20 && RUST_LOG=info SP1_PROVER=network ./target/release/operator fixtures update-client --trusted-block $TRUSTED_HEIGHT --target-block $TARGET_HEIGHT -p groth16 -o 'test/sp1-ics07/fixtures/update_client_fixture-groth16.json'" \ + "sleep 40 && RUST_LOG=info SP1_PROVER=network ./target/release/operator fixtures update-client-and-membership --key-paths clients/07-tendermint-0/clientState,clients/07-tendermint-001/clientState --trusted-block $TRUSTED_HEIGHT --target-block $TARGET_HEIGHT -o 'test/sp1-ics07/fixtures/uc_and_memberships_fixture-plonk.json'" \ + "sleep 60 && RUST_LOG=info SP1_PROVER=network ./target/release/operator fixtures update-client-and-membership --key-paths clients/07-tendermint-0/clientState,clients/07-tendermint-001/clientState --trusted-block $TRUSTED_HEIGHT --target-block $TARGET_HEIGHT -p groth16 -o 'test/sp1-ics07/fixtures/uc_and_memberships_fixture-groth16.json'" \ + "sleep 80 && RUST_LOG=info SP1_PROVER=network ./target/release/operator fixtures membership --key-paths clients/07-tendermint-0/clientState,clients/07-tendermint-001/clientState --trusted-block $TRUSTED_HEIGHT -o 'test/sp1-ics07/fixtures/memberships_fixture-plonk.json'" \ + "sleep 100 && RUST_LOG=info SP1_PROVER=network ./target/release/operator fixtures membership --key-paths clients/07-tendermint-0/clientState,clients/07-tendermint-001/clientState --trusted-block $TRUSTED_HEIGHT -p groth16 -o 'test/sp1-ics07/fixtures/memberships_fixture-groth16.json'" + cd e2e/interchaintestv8 && RUST_LOG=info SP1_PROVER=network GENERATE_FIXTURES=true go test -v -run '^TestWithSP1ICS07TendermintTestSuite/TestDoubleSignMisbehaviour_Plonk$' -timeout 40m + cd e2e/interchaintestv8 && RUST_LOG=info SP1_PROVER=network GENERATE_FIXTURES=true go test -v -run '^TestWithSP1ICS07TendermintTestSuite/TestBreakingTimeMonotonicityMisbehaviour_Groth16' -timeout 40m + cd e2e/interchaintestv8 && RUST_LOG=info SP1_PROVER=network GENERATE_FIXTURES=true go test -v -run '^TestWithSP1ICS07TendermintTestSuite/Test100Membership_Groth16' -timeout 40m + cd e2e/interchaintestv8 && RUST_LOG=info SP1_PROVER=network GENERATE_FIXTURES=true go test -v -run '^TestWithSP1ICS07TendermintTestSuite/Test25Membership_Plonk' -timeout 40m + @echo "Fixtures generated at 'test/sp1-ics07/fixtures'" + protoImageName := "ghcr.io/cosmos/proto-builder:0.14.0" DOCKER := `which docker` diff --git a/package.json b/package.json index 09e30958..2d96a44c 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "devDependencies": { "sp1-contracts": "github:succinctlabs/sp1-contracts#275691af9bfaf67158f6df1f4c3c1646eb03eed0", "forge-std": "github:foundry-rs/forge-std#v1.9.4", - "@cosmos/sp1-ics07-tendermint": "github:cosmos/sp1-ics07-tendermint#f67f5fec9423a4744092ee98b62bc60e3354f223", "solhint": "^5.0.3", "@defi-wonderland/natspec-smells": "^1.1.5" }, @@ -27,7 +26,7 @@ ], "private": true, "scripts": { - "lint:sol": "forge fmt --check && bun solhint -w 0 '{script,contracts,test}/**/*.sol' && bun natspec-smells --include 'contracts/**/*.sol'", + "lint:sol": "forge fmt --check && bun solhint -w 0 '{scripts,contracts,test}/**/*.sol'", "test:coverage": "forge coverage", "test:coverage:report": "forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage" } diff --git a/packages/prover/Cargo.toml b/packages/prover/Cargo.toml new file mode 100644 index 000000000..0de606e4 --- /dev/null +++ b/packages/prover/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "sp1-ics07-tendermint-prover" +version = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +license = { workspace = true } + +[dependencies] +sp1-sdk = { workspace = true } + +ibc-eureka-solidity-types = { workspace = true } + +bincode = { workspace = true } +serde_cbor = { workspace = true, features = ["std"] } + +tracing = { workspace = true } + +ibc-proto = { workspace = true } + +ibc-client-tendermint-types = { workspace = true, features = ["serde"] } +ibc-core-commitment-types = { workspace = true } + +[build-dependencies] +sp1-helper = { workspace = true } diff --git a/packages/prover/README.md b/packages/prover/README.md new file mode 100644 index 000000000..db7028e3 --- /dev/null +++ b/packages/prover/README.md @@ -0,0 +1,3 @@ +# Prover Library for SP1 ICS-07 Tendermint + +This crate provides a prover library for `sp1-ics07-tendermint`. diff --git a/packages/prover/build.rs b/packages/prover/build.rs new file mode 100644 index 000000000..ecf7f8d0 --- /dev/null +++ b/packages/prover/build.rs @@ -0,0 +1,38 @@ +use sp1_helper::{build_program_with_args, BuildArgs}; + +// Build script to build the programs if they change. +// Requires SP1 toolchain to be installed. +fn main() { + // Build the update-client program. + build_program_with_args( + "../../programs/sp1-programs/update-client", + BuildArgs { + elf_name: "update-client-riscv32im-succinct-zkvm-elf".to_string(), + ..Default::default() + }, + ); + // Build the membership program. + build_program_with_args( + "../../programs/sp1-programs/membership", + BuildArgs { + elf_name: "membership-riscv32im-succinct-zkvm-elf".to_string(), + ..Default::default() + }, + ); + // Build the uc-and-membership program. + build_program_with_args( + "../../programs/sp1-programs/uc-and-membership", + BuildArgs { + elf_name: "uc-and-membership-riscv32im-succinct-zkvm-elf".to_string(), + ..Default::default() + }, + ); + // Build the misbehaviour program. + build_program_with_args( + "../../programs/sp1-programs/misbehaviour", + BuildArgs { + elf_name: "misbehaviour-riscv32im-succinct-zkvm-elf".to_string(), + ..Default::default() + }, + ) +} diff --git a/packages/prover/src/lib.rs b/packages/prover/src/lib.rs new file mode 100644 index 000000000..66e0d5f8 --- /dev/null +++ b/packages/prover/src/lib.rs @@ -0,0 +1,5 @@ +#![doc = include_str!("../README.md")] +#![deny(clippy::nursery, clippy::pedantic, warnings, missing_docs)] + +pub mod programs; +pub mod prover; diff --git a/packages/prover/src/programs.rs b/packages/prover/src/programs.rs new file mode 100644 index 000000000..7bdd7224 --- /dev/null +++ b/packages/prover/src/programs.rs @@ -0,0 +1,49 @@ +//! Programs for `sp1-ics07-tendermint`. + +use sp1_sdk::{MockProver, Prover, SP1VerifyingKey}; + +/// Trait for SP1 ICS07 Tendermint programs. +pub trait SP1Program { + /// The ELF file for the program. + const ELF: &'static [u8]; + + /// Get the verifying key for the program using [`MockProver`]. + #[must_use] + fn get_vkey() -> SP1VerifyingKey { + let mock_prover = MockProver::new(); + let (_, vkey) = mock_prover.setup(Self::ELF); + vkey + } +} + +/// SP1 ICS07 Tendermint update client program. +pub struct UpdateClientProgram; + +/// SP1 ICS07 Tendermint verify (non)membership program. +pub struct MembershipProgram; + +/// SP1 ICS07 Tendermint update client and verify (non)membership program. +pub struct UpdateClientAndMembershipProgram; + +/// SP1 ICS07 Tendermint misbehaviour program. +pub struct MisbehaviourProgram; + +impl SP1Program for UpdateClientProgram { + const ELF: &'static [u8] = + include_bytes!("../../../elf/update-client-riscv32im-succinct-zkvm-elf"); +} + +impl SP1Program for MembershipProgram { + const ELF: &'static [u8] = + include_bytes!("../../../elf/membership-riscv32im-succinct-zkvm-elf"); +} + +impl SP1Program for UpdateClientAndMembershipProgram { + const ELF: &'static [u8] = + include_bytes!("../../../elf/uc-and-membership-riscv32im-succinct-zkvm-elf"); +} + +impl SP1Program for MisbehaviourProgram { + const ELF: &'static [u8] = + include_bytes!("../../../elf/misbehaviour-riscv32im-succinct-zkvm-elf"); +} diff --git a/packages/prover/src/prover.rs b/packages/prover/src/prover.rs new file mode 100644 index 000000000..4835bf13 --- /dev/null +++ b/packages/prover/src/prover.rs @@ -0,0 +1,240 @@ +//! Prover for SP1 ICS07 Tendermint programs. + +use crate::programs::{ + MembershipProgram, MisbehaviourProgram, SP1Program, UpdateClientAndMembershipProgram, + UpdateClientProgram, +}; +use ibc_client_tendermint_types::{Header, Misbehaviour}; +use ibc_core_commitment_types::merkle::MerkleProof; +use ibc_eureka_solidity_types::sp1_ics07::{ + IICS07TendermintMsgs::{ClientState as SolClientState, ConsensusState as SolConsensusState}, + ISP1Msgs::SupportedZkAlgorithm, +}; +use ibc_proto::Protobuf; +use sp1_sdk::{ProverClient, SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin, SP1VerifyingKey}; + +/// A prover for for [`SP1Program`] programs. +#[allow(clippy::module_name_repetitions)] +pub struct SP1ICS07TendermintProver { + /// [`sp1_sdk::ProverClient`] for generating proofs. + pub prover_client: ProverClient, + /// The proving key. + pub pkey: SP1ProvingKey, + /// The verifying key. + pub vkey: SP1VerifyingKey, + /// The proof type. + pub proof_type: SupportedProofType, + _phantom: std::marker::PhantomData, +} + +/// The supported proof types. +#[derive(Clone, Debug, Copy)] +pub enum SupportedProofType { + /// Groth16 proof. + Groth16, + /// Plonk proof. + Plonk, +} + +impl SP1ICS07TendermintProver { + /// Create a new prover. + #[must_use] + #[tracing::instrument(skip_all)] + pub fn new(proof_type: SupportedProofType) -> Self { + tracing::info!("Initializing SP1 ProverClient..."); + let prover_client = ProverClient::new(); + let (pkey, vkey) = prover_client.setup(T::ELF); + tracing::info!("SP1 ProverClient initialized"); + Self { + prover_client, + pkey, + vkey, + proof_type, + _phantom: std::marker::PhantomData, + } + } + + /// Prove the given input. + /// # Panics + /// If the proof cannot be generated or validated. + #[must_use] + pub fn prove(&self, stdin: SP1Stdin) -> SP1ProofWithPublicValues { + // Generate the proof. Depending on SP1_PROVER env variable, this may be a mock, local or + // network proof. + let proof = match self.proof_type { + SupportedProofType::Plonk => self + .prover_client + .prove(&self.pkey, stdin) + .plonk() + .run() + .expect("proving failed"), + SupportedProofType::Groth16 => self + .prover_client + .prove(&self.pkey, stdin) + .groth16() + .run() + .expect("proving failed"), + }; + + self.prover_client + .verify(&proof, &self.vkey) + .expect("verification failed"); + + proof + } +} + +impl SP1ICS07TendermintProver { + /// Generate a proof of an update from `trusted_consensus_state` to a proposed header. + /// + /// # Panics + /// Panics if the inputs cannot be encoded, the proof cannot be generated or the proof is + /// invalid. + #[must_use] + pub fn generate_proof( + &self, + client_state: &SolClientState, + trusted_consensus_state: &SolConsensusState, + proposed_header: &Header, + time: u64, + ) -> SP1ProofWithPublicValues { + // Encode the inputs into our program. + let encoded_1 = bincode::serialize(client_state).unwrap(); + let encoded_2 = bincode::serialize(&trusted_consensus_state).unwrap(); + let encoded_3 = serde_cbor::to_vec(proposed_header).unwrap(); + let encoded_4 = time.to_le_bytes().into(); + // TODO: find an encoding that works for all the structs above. + + // Write the encoded light blocks to stdin. + let mut stdin = SP1Stdin::new(); + stdin.write_vec(encoded_1); + stdin.write_vec(encoded_2); + stdin.write_vec(encoded_3); + stdin.write_vec(encoded_4); + + self.prove(stdin) + } +} + +impl SP1ICS07TendermintProver { + /// Generate a proof of verify (non)membership for multiple key-value pairs. + /// + /// # Panics + /// Panics if the proof cannot be generated or the proof is invalid. + #[must_use] + pub fn generate_proof( + &self, + commitment_root: &[u8], + kv_proofs: Vec<(Vec>, Vec, MerkleProof)>, + ) -> SP1ProofWithPublicValues { + assert!(!kv_proofs.is_empty(), "No key-value pairs to prove"); + let len = u8::try_from(kv_proofs.len()).expect("too many key-value pairs"); + + let mut stdin = SP1Stdin::new(); + stdin.write_slice(commitment_root); + stdin.write_vec(vec![len]); + for (path, value, proof) in kv_proofs { + stdin.write_vec(bincode::serialize(&path).unwrap()); + stdin.write_vec(value); + stdin.write_vec(proof.encode_vec()); + } + + self.prove(stdin) + } +} + +impl SP1ICS07TendermintProver { + /// Generate a proof of an update from `trusted_consensus_state` to a proposed header and + /// verify (non)membership for multiple key-value pairs on the commitment root of + /// `proposed_header`. + /// + /// # Panics + /// Panics if the inputs cannot be encoded, the proof cannot be generated or the proof is + /// invalid. + #[must_use] + pub fn generate_proof( + &self, + client_state: &SolClientState, + trusted_consensus_state: &SolConsensusState, + proposed_header: &Header, + time: u64, + kv_proofs: Vec<(Vec>, Vec, MerkleProof)>, + ) -> SP1ProofWithPublicValues { + assert!(!kv_proofs.is_empty(), "No key-value pairs to prove"); + let len = u8::try_from(kv_proofs.len()).expect("too many key-value pairs"); + // Encode the inputs into our program. + let encoded_1 = bincode::serialize(client_state).unwrap(); + let encoded_2 = bincode::serialize(&trusted_consensus_state).unwrap(); + // NOTE: The Header struct is not deserializable by bincode, so we use CBOR instead. + let encoded_3 = serde_cbor::to_vec(proposed_header).unwrap(); + let encoded_4 = time.to_le_bytes().into(); + // TODO: find an encoding that works for all the structs above. + + // Write the encoded light blocks to stdin. + let mut stdin = SP1Stdin::new(); + stdin.write_vec(encoded_1); + stdin.write_vec(encoded_2); + stdin.write_vec(encoded_3); + stdin.write_vec(encoded_4); + stdin.write_vec(vec![len]); + for (path, value, proof) in kv_proofs { + stdin.write_vec(bincode::serialize(&path).unwrap()); + stdin.write_vec(value); + stdin.write_vec(proof.encode_vec()); + } + + self.prove(stdin) + } +} + +impl SP1ICS07TendermintProver { + /// Generate a proof of a misbehaviour. + /// + /// # Panics + /// Panics if the proof cannot be generated or the proof is invalid. + #[must_use] + pub fn generate_proof( + &self, + client_state: &SolClientState, + misbehaviour: &Misbehaviour, + trusted_consensus_state_1: &SolConsensusState, + trusted_consensus_state_2: &SolConsensusState, + time: u64, + ) -> SP1ProofWithPublicValues { + let encoded_1 = bincode::serialize(client_state).unwrap(); + let encoded_2 = serde_cbor::to_vec(misbehaviour).unwrap(); + let encoded_3 = bincode::serialize(trusted_consensus_state_1).unwrap(); + let encoded_4 = bincode::serialize(trusted_consensus_state_2).unwrap(); + let encoded_5 = time.to_le_bytes().into(); + + let mut stdin = SP1Stdin::new(); + stdin.write_vec(encoded_1); + stdin.write_vec(encoded_2); + stdin.write_vec(encoded_3); + stdin.write_vec(encoded_4); + stdin.write_vec(encoded_5); + + self.prove(stdin) + } +} + +impl From for SupportedZkAlgorithm { + fn from(proof_type: SupportedProofType) -> Self { + match proof_type { + SupportedProofType::Groth16 => Self::from(0), + SupportedProofType::Plonk => Self::from(1), + } + } +} + +impl TryFrom for SupportedProofType { + type Error = String; + + fn try_from(n: u8) -> Result { + match n { + 0 => Ok(Self::Groth16), + 1 => Ok(Self::Plonk), + n => Err(format!("Unsupported proof type: {n}")), + } + } +} diff --git a/packages/relayer-lib/Cargo.toml b/packages/relayer-lib/Cargo.toml index 7593509a..09804e60 100644 --- a/packages/relayer-lib/Cargo.toml +++ b/packages/relayer-lib/Cargo.toml @@ -8,26 +8,25 @@ license = { workspace = true } [features] default = ["sp1-toolchain"] # sp1-toolchain requires sp1 toolchain to be installed to build -sp1-toolchain = ["dep:sp1-ics07-tendermint-solidity", "dep:sp1-ics07-tendermint-prover", "dep:sp1-ics07-tendermint-utils"] +sp1-toolchain = ["dep:sp1-ics07-tendermint-prover", "dep:sp1-ics07-tendermint-utils"] [dependencies] ibc-eureka-solidity-types = { workspace = true, features = ["rpc"] } -sp1-ics07-tendermint-solidity = { workspace = true, features = ["rpc"], optional = true } -sp1-ics07-tendermint-prover = { workspace = true, optional = true } -sp1-ics07-tendermint-utils = { workspace = true, optional = true } +sp1-ics07-tendermint-prover = { workspace = true, optional = true } +sp1-ics07-tendermint-utils = { workspace = true, optional = true } -serde = { workspace = true, features = ["derive"] } +serde = { workspace = true, features = ["derive"] } async-trait = { workspace = true } -anyhow = { workspace = true, features = ["std"] } -futures = { workspace = true, default-features = true } +anyhow = { workspace = true, features = ["std"] } +futures = { workspace = true, default-features = true } -tendermint = { workspace = true, features = ["std"] } +tendermint = { workspace = true, features = ["std"] } tendermint-rpc = { workspace = true, features = ["http-client"] } ibc-core-host-types = { workspace = true } -alloy = { workspace = true, features = ["full", "node-bindings"] } +alloy = { workspace = true, features = ["full", "node-bindings"] } alloy-contract = { workspace = true } sp1-sdk = { workspace = true } diff --git a/packages/relayer-lib/src/tx_builder/eth_eureka.rs b/packages/relayer-lib/src/tx_builder/eth_eureka.rs index 5d39c6f1..bd22a0ee 100644 --- a/packages/relayer-lib/src/tx_builder/eth_eureka.rs +++ b/packages/relayer-lib/src/tx_builder/eth_eureka.rs @@ -21,6 +21,12 @@ use ibc_eureka_solidity_types::{ IICS02ClientMsgs::Height, IICS26RouterMsgs::{MsgAckPacket, MsgRecvPacket, MsgTimeoutPacket}, }, + sp1_ics07::{ + sp1_ics07_tendermint, + IICS07TendermintMsgs::ClientState, + IMembershipMsgs::{MembershipProof, SP1MembershipAndUpdateClientProof}, + ISP1Msgs::SP1Proof, + }, }; // Re-export the `SupportedProofType` enum. pub use sp1_ics07_tendermint_prover::prover::SupportedProofType; @@ -28,12 +34,6 @@ use sp1_ics07_tendermint_prover::{ programs::UpdateClientAndMembershipProgram, prover::SP1ICS07TendermintProver, }; -use sp1_ics07_tendermint_solidity::{ - sp1_ics07_tendermint, - IICS07TendermintMsgs::ClientState, - IMembershipMsgs::{MembershipProof, SP1MembershipAndUpdateClientProof}, - ISP1Msgs::SP1Proof, -}; use sp1_ics07_tendermint_utils::{ light_block::LightBlockExt, merkle::convert_tm_to_ics_merkle_proof, rpc::TendermintRpcExt, }; diff --git a/packages/solidity/Cargo.toml b/packages/solidity/Cargo.toml index 0271a257..3433ecde 100644 --- a/packages/solidity/Cargo.toml +++ b/packages/solidity/Cargo.toml @@ -10,6 +10,13 @@ rpc = ["dep:alloy-contract", "dep:hex"] [dependencies] alloy-sol-types = { workspace = true, features = ["json"] } -alloy-contract = { workspace = true, optional = true } -serde = { workspace = true } -hex = { workspace = true, optional = true } +alloy-contract = { workspace = true, optional = true } +serde = { workspace = true } +hex = { workspace = true, optional = true } + +tendermint-light-client-verifier = { workspace = true } +ibc-client-tendermint-types = { workspace = true } +tendermint = { workspace = true } +ibc-core-commitment-types = { workspace = true } +ibc-core-client-types = { workspace = true } +time = { workspace = true } diff --git a/packages/solidity/src/lib.rs b/packages/solidity/src/lib.rs index 3d90df66..6a474adf 100644 --- a/packages/solidity/src/lib.rs +++ b/packages/solidity/src/lib.rs @@ -5,3 +5,4 @@ pub mod ibc_store; pub mod ics02; pub mod ics26; +pub mod sp1_ics07; diff --git a/packages/solidity/src/sp1_ics07.rs b/packages/solidity/src/sp1_ics07.rs new file mode 100644 index 000000000..d641f56b --- /dev/null +++ b/packages/solidity/src/sp1_ics07.rs @@ -0,0 +1,138 @@ +use alloy_sol_types::SolValue; +use ibc_client_tendermint_types::ConsensusState as ICS07TendermintConsensusState; +use ibc_core_commitment_types::commitment::CommitmentRoot; +use tendermint::{hash::Algorithm, Time}; +use tendermint_light_client_verifier::types::{Hash, TrustThreshold as TendermintTrustThreshold}; +use time::OffsetDateTime; + +#[cfg(feature = "rpc")] +alloy_sol_types::sol!( + #[sol(rpc)] + #[derive(Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)] + #[allow(missing_docs, clippy::pedantic, warnings)] + sp1_ics07_tendermint, + "../../abi/SP1ICS07Tendermint.json" +); + +// NOTE: The riscv program won't compile with the `rpc` features. +#[cfg(not(feature = "rpc"))] +alloy_sol_types::sol!( + #[derive(Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)] + #[allow(missing_docs, clippy::pedantic)] + sp1_ics07_tendermint, + "../../abi/SP1ICS07Tendermint.json" +); + +#[cfg(feature = "rpc")] +impl ISP1Msgs::SP1Proof { + /// Create a new [`sp1_ics07_tendermint::SP1Proof`] instance. + /// + /// # Panics + /// Panics if the vkey is not a valid hex string, or if the bytes cannot be decoded. + #[must_use] + pub fn new(vkey: &str, proof: Vec, public_values: Vec) -> Self { + let stripped = vkey.strip_prefix("0x").expect("failed to strip prefix"); + let vkey_bytes: [u8; 32] = hex::decode(stripped) + .expect("failed to decode vkey") + .try_into() + .expect("invalid vkey length"); + Self { + vKey: vkey_bytes.into(), + proof: proof.into(), + publicValues: public_values.into(), + } + } +} + +#[allow(clippy::fallible_impl_from)] +impl From for TendermintTrustThreshold { + fn from(trust_threshold: IICS07TendermintMsgs::TrustThreshold) -> Self { + Self::new( + trust_threshold.numerator.into(), + trust_threshold.denominator.into(), + ) + .unwrap() + } +} + +impl TryFrom for IICS07TendermintMsgs::TrustThreshold { + type Error = >::Error; + + fn try_from(trust_threshold: TendermintTrustThreshold) -> Result { + Ok(Self { + numerator: trust_threshold.numerator().try_into()?, + denominator: trust_threshold.denominator().try_into()?, + }) + } +} + +#[allow(clippy::fallible_impl_from)] +impl From for IICS07TendermintMsgs::ConsensusState { + fn from(ics07_tendermint_consensus_state: ICS07TendermintConsensusState) -> Self { + let root: [u8; 32] = ics07_tendermint_consensus_state + .root + .into_vec() + .try_into() + .unwrap(); + let next_validators_hash: [u8; 32] = ics07_tendermint_consensus_state + .next_validators_hash + .as_bytes() + .try_into() + .unwrap(); + Self { + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + timestamp: ics07_tendermint_consensus_state.timestamp.unix_timestamp() as u64, + root: root.into(), + nextValidatorsHash: next_validators_hash.into(), + } + } +} + +#[allow(clippy::fallible_impl_from)] +impl From for ICS07TendermintConsensusState { + fn from(consensus_state: IICS07TendermintMsgs::ConsensusState) -> Self { + let time = + OffsetDateTime::from_unix_timestamp(consensus_state.timestamp.try_into().unwrap()) + .unwrap(); + let seconds = time.unix_timestamp(); + let nanos = time.nanosecond(); + Self { + timestamp: Time::from_unix_timestamp(seconds, nanos).unwrap(), + root: CommitmentRoot::from_bytes(&consensus_state.root.0), + next_validators_hash: Hash::from_bytes( + Algorithm::Sha256, + &consensus_state.nextValidatorsHash.0, + ) + .unwrap(), + } + } +} + +impl From for IMembershipMsgs::MembershipProof { + fn from(proof: IMembershipMsgs::SP1MembershipProof) -> Self { + Self { + proofType: 0, + proof: proof.abi_encode().into(), + } + } +} + +impl From for IMembershipMsgs::MembershipProof { + fn from(proof: IMembershipMsgs::SP1MembershipAndUpdateClientProof) -> Self { + Self { + proofType: 1, + proof: proof.abi_encode().into(), + } + } +} + +impl TryFrom for IICS02ClientMsgs::Height { + type Error = >::Error; + + fn try_from(height: ibc_core_client_types::Height) -> Result { + Ok(Self { + revisionNumber: height.revision_number().try_into()?, + revisionHeight: height.revision_height().try_into()?, + }) + } +} diff --git a/packages/utils/Cargo.toml b/packages/utils/Cargo.toml new file mode 100644 index 000000000..2e448600 --- /dev/null +++ b/packages/utils/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "sp1-ics07-tendermint-utils" +version = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +license = { workspace = true } + +[dependencies] +anyhow = { workspace = true, features = ["std"] } +async-trait = { workspace = true } +serde = { workspace = true } +prost = { workspace = true } +cosmos-sdk-proto = { workspace = true } +tendermint-rpc = { workspace = true } +ibc-eureka-solidity-types = { workspace = true } +tendermint-light-client-verifier = { workspace = true } +ibc-client-tendermint-types = { workspace = true } +ibc-core-client-types = { workspace = true } +ibc-core-host-types = { workspace = true, features = ["std"] } +tendermint = { workspace = true } +ibc-core-commitment-types = { workspace = true } +alloy = { workspace = true, features = ["full", "node-bindings"] } diff --git a/packages/utils/README.md b/packages/utils/README.md new file mode 100644 index 000000000..b1db7887 --- /dev/null +++ b/packages/utils/README.md @@ -0,0 +1,3 @@ +# Utilities for SP1 ICS-07 Tendermint + +This crate provides some utility functions for `sp1-ics07-tendermint`. diff --git a/packages/utils/src/eth.rs b/packages/utils/src/eth.rs new file mode 100644 index 000000000..1027ccc2 --- /dev/null +++ b/packages/utils/src/eth.rs @@ -0,0 +1,21 @@ +//! Helpers for interacting with EVM. + +use std::env; + +use alloy::{network::EthereumWallet, signers::local::PrivateKeySigner}; + +/// Create an Ethereum wallet from the `PRIVATE_KEY` environment variable. +/// +/// # Panics +/// Panics if the `PRIVATE_KEY` environment variable is not set. +/// Panics if the `PRIVATE_KEY` environment variable is not a valid private key. +#[must_use] +pub fn wallet_from_env() -> EthereumWallet { + let mut private_key = env::var("PRIVATE_KEY").expect("PRIVATE_KEY not set"); + if let Some(stripped) = private_key.strip_prefix("0x") { + private_key = stripped.to_string(); + } + + let signer: PrivateKeySigner = private_key.parse().unwrap(); + EthereumWallet::from(signer) +} diff --git a/packages/utils/src/lib.rs b/packages/utils/src/lib.rs new file mode 100644 index 000000000..b8797680 --- /dev/null +++ b/packages/utils/src/lib.rs @@ -0,0 +1,7 @@ +#![doc = include_str!("../README.md")] +#![deny(clippy::nursery, clippy::pedantic, warnings, missing_docs)] + +pub mod eth; +pub mod light_block; +pub mod merkle; +pub mod rpc; diff --git a/packages/utils/src/light_block.rs b/packages/utils/src/light_block.rs new file mode 100644 index 000000000..137feaed --- /dev/null +++ b/packages/utils/src/light_block.rs @@ -0,0 +1,94 @@ +//! Provides helpers for deriving other types from `LightBlock`. + +use ibc_client_tendermint_types::{ConsensusState, Header}; +use ibc_core_client_types::Height as IbcHeight; +use ibc_core_commitment_types::commitment::CommitmentRoot; +use ibc_core_host_types::{error::IdentifierError, identifiers::ChainId}; +use ibc_eureka_solidity_types::sp1_ics07::{ + IICS02ClientMsgs::Height, + IICS07TendermintMsgs::{ClientState, TrustThreshold}, + ISP1Msgs::SupportedZkAlgorithm, +}; +use std::str::FromStr; +use tendermint_light_client_verifier::types::LightBlock; + +/// Extension trait for [`LightBlock`] that provides additional methods for converting to other +/// types. +#[allow(clippy::module_name_repetitions)] +pub trait LightBlockExt { + /// Convert the [`LightBlock`] to a new solidity [`ClientState`]. + /// + /// # Errors + /// Returns an error if the chain identifier or height cannot be parsed. + fn to_sol_client_state( + &self, + trust_level: TrustThreshold, + unbonding_period: u32, + trusting_period: u32, + zk_algorithm: SupportedZkAlgorithm, + ) -> anyhow::Result; + /// Convert the [`LightBlock`] to a new [`ConsensusState`]. + #[must_use] + fn to_consensus_state(&self) -> ConsensusState; + /// Convert the [`LightBlock`] to a new [`Header`]. + /// + /// # Panics + /// Panics if the `trusted_height` is zero. + #[must_use] + fn into_header(self, trusted_light_block: &LightBlock) -> Header; + /// Get the chain identifier from the [`LightBlock`]. + /// + /// # Errors + /// Returns an error if the chain identifier cannot be parsed. + fn chain_id(&self) -> Result; +} + +impl LightBlockExt for LightBlock { + fn to_sol_client_state( + &self, + trust_level: TrustThreshold, + unbonding_period: u32, + trusting_period: u32, + zk_algorithm: SupportedZkAlgorithm, + ) -> anyhow::Result { + let chain_id = ChainId::from_str(self.signed_header.header.chain_id.as_str())?; + Ok(ClientState { + chainId: chain_id.to_string(), + trustLevel: trust_level, + latestHeight: Height { + revisionNumber: chain_id.revision_number().try_into()?, + revisionHeight: self.height().value().try_into()?, + }, + isFrozen: false, + zkAlgorithm: zk_algorithm.into(), + unbondingPeriod: unbonding_period, + trustingPeriod: trusting_period, + }) + } + + fn to_consensus_state(&self) -> ConsensusState { + ConsensusState { + timestamp: self.signed_header.header.time, + root: CommitmentRoot::from_bytes(self.signed_header.header.app_hash.as_bytes()), + next_validators_hash: self.signed_header.header.next_validators_hash, + } + } + + fn into_header(self, trusted_light_block: &LightBlock) -> Header { + let trusted_revision_number = + ChainId::from_str(trusted_light_block.signed_header.header.chain_id.as_str()) + .unwrap() + .revision_number(); + let trusted_block_height = trusted_light_block.height().value(); + Header { + signed_header: self.signed_header, + validator_set: self.validators, + trusted_height: IbcHeight::new(trusted_revision_number, trusted_block_height).unwrap(), + trusted_next_validator_set: trusted_light_block.next_validators.clone(), + } + } + + fn chain_id(&self) -> Result { + ChainId::from_str(self.signed_header.header.chain_id.as_str()) + } +} diff --git a/packages/utils/src/merkle.rs b/packages/utils/src/merkle.rs new file mode 100644 index 000000000..37976df0 --- /dev/null +++ b/packages/utils/src/merkle.rs @@ -0,0 +1,22 @@ +//! This module defines the conversion functions between Tendermint and ICS Merkle proofs. + +use ibc_core_commitment_types::{merkle::MerkleProof, proto::ics23::CommitmentProof}; +use tendermint::merkle::proof::ProofOps; + +/// Convert a Tendermint proof to an ICS Merkle proof. +/// +/// # Errors +/// Returns a decoding error if the prost merge. +pub fn convert_tm_to_ics_merkle_proof( + tm_proof: &ProofOps, +) -> Result { + let mut proofs = Vec::with_capacity(tm_proof.ops.len()); + + for op in &tm_proof.ops { + let mut parsed = CommitmentProof { proof: None }; + prost::Message::merge(&mut parsed, op.data.as_slice())?; + proofs.push(parsed); + } + + Ok(MerkleProof { proofs }) +} diff --git a/packages/utils/src/rpc.rs b/packages/utils/src/rpc.rs new file mode 100644 index 000000000..f6908185 --- /dev/null +++ b/packages/utils/src/rpc.rs @@ -0,0 +1,118 @@ +//! RPC client for interacting with a Tendermint node. + +use core::str::FromStr; +use std::{collections::HashMap, env}; + +use anyhow::Result; + +use cosmos_sdk_proto::{ + cosmos::staking::v1beta1::{Params, QueryParamsRequest, QueryParamsResponse}, + prost::Message, + traits::MessageExt, +}; +use tendermint::{block::signed_header::SignedHeader, validator::Set}; +use tendermint_light_client_verifier::types::{LightBlock, ValidatorSet}; +use tendermint_rpc::{Client, HttpClient, Paging, Url}; + +/// An extension trait for [`HttpClient`] that provides additional methods for +/// obtaining light blocks. +#[async_trait::async_trait] +pub trait TendermintRpcExt { + /// Creates a new instance of the Tendermint RPC client from the environment variables. + /// + /// # Panics + /// Panics if the `TENDERMINT_RPC_URL` environment variable is not set or if the URL is + /// invalid. + #[must_use] + fn from_env() -> Self; + /// Gets a light block for a specific block height. + /// If `block_height` is `None`, the latest block is fetched. + /// + /// # Errors + /// Returns an error if the RPC request fails or if the response cannot be parsed. + async fn get_light_block(&self, block_height: Option) -> Result; + /// Queries the Cosmos SDK for staking parameters. + async fn sdk_staking_params(&self) -> Result; +} + +#[async_trait::async_trait] +impl TendermintRpcExt for HttpClient { + fn from_env() -> Self { + Self::new( + Url::from_str(&env::var("TENDERMINT_RPC_URL").expect("TENDERMINT_RPC_URL not set")) + .expect("Failed to parse URL"), + ) + .expect("Failed to create HTTP client") + } + + async fn get_light_block(&self, block_height: Option) -> Result { + let peer_id = self.status().await?.node_info.id; + let commit_response; + let height; + if let Some(block_height) = block_height { + commit_response = self.commit(block_height).await?; + height = block_height; + } else { + commit_response = self.latest_commit().await?; + height = commit_response + .signed_header + .header + .height + .value() + .try_into()?; + } + let mut signed_header = commit_response.signed_header; + + let validator_response = self.validators(height, Paging::All).await?; + let validators = Set::new(validator_response.validators, None); + + let next_validator_response = self.validators(height + 1, Paging::All).await?; + let next_validators = Set::new(next_validator_response.validators, None); + + sort_signatures_by_validators_power_desc(&mut signed_header, &validators); + Ok(LightBlock::new( + signed_header, + validators, + next_validators, + peer_id, + )) + } + + async fn sdk_staking_params(&self) -> Result { + let abci_resp = self + .abci_query( + Some("/cosmos.staking.v1beta1.Query/Params".to_string()), + QueryParamsRequest::default().to_bytes()?, + None, + false, + ) + .await?; + QueryParamsResponse::decode(abci_resp.value.as_slice())? + .params + .ok_or_else(|| anyhow::anyhow!("No staking params found")) + } +} + +/// Sorts the signatures in the signed header based on the descending order of validators' power. +fn sort_signatures_by_validators_power_desc( + signed_header: &mut SignedHeader, + validators_set: &ValidatorSet, +) { + let validator_powers: HashMap<_, _> = validators_set + .validators() + .iter() + .map(|v| (v.address, v.power())) + .collect(); + + signed_header.commit.signatures.sort_by(|a, b| { + let power_a = a + .validator_address() + .and_then(|addr| validator_powers.get(&addr)) + .unwrap_or(&0); + let power_b = b + .validator_address() + .and_then(|addr| validator_powers.get(&addr)) + .unwrap_or(&0); + power_b.cmp(power_a) + }); +} diff --git a/programs/operator/Cargo.toml b/programs/operator/Cargo.toml new file mode 100644 index 000000000..91c46a3e --- /dev/null +++ b/programs/operator/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "sp1-ics07-tendermint-operator" +version = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +license = { workspace = true } + +[dependencies] +sp1-sdk = { workspace = true, default-features = true } + +serde_json = { workspace = true } +serde = { workspace = true } +serde_with = { workspace = true, features = ["hex", "macros"] } +hex = { workspace = true } +subtle-encoding = { workspace = true } + +reqwest = { workspace = true } +tokio = { workspace = true } +futures = { workspace = true } +dotenv = { workspace = true } +anyhow = { workspace = true } +clap = { workspace = true } +tracing = { workspace = true, default-features = true } + +cosmos-sdk-proto = { workspace = true } +ibc-proto = { workspace = true } + +tendermint = { workspace = true } +tendermint-rpc = { workspace = true, features = ["http-client"] } +tendermint-light-client-verifier = { workspace = true } + +ibc-client-tendermint-types = { workspace = true } +ibc-core-client-types = { workspace = true } +ibc-core-commitment-types = { workspace = true } + +ibc-eureka-solidity-types = { workspace = true, features = ["rpc"] } +sp1-ics07-tendermint-utils = { workspace = true } +sp1-ics07-tendermint-prover = { workspace = true } + +alloy-sol-types = { workspace = true } +alloy-primitives = { workspace = true } +alloy = { workspace = true, features = ["full", "node-bindings"] } diff --git a/programs/operator/src/bin/operator.rs b/programs/operator/src/bin/operator.rs new file mode 100644 index 000000000..6091e86f --- /dev/null +++ b/programs/operator/src/bin/operator.rs @@ -0,0 +1,34 @@ +use clap::Parser; +use sp1_ics07_tendermint_operator::{ + cli::command::{fixtures, Commands, OperatorCli}, + runners::{ + self, + fixtures::{membership, misbehaviour, uc_and_mem, update_client}, + }, +}; +use sp1_sdk::utils::setup_logger; + +/// An implementation of a Tendermint Light Client operator that will poll an onchain Tendermint +/// light client and generate a proof of the transition from the latest block in the contract to the +/// latest block on the chain. Then, submits the proof to the contract and updates the contract with +/// the latest block hash and height. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + setup_logger(); + + if dotenv::dotenv().is_err() { + tracing::warn!("No .env file found"); + } + + let cli = OperatorCli::parse(); + match cli.command { + Commands::Start(args) => runners::operator::run(args).await, + Commands::Genesis(args) => runners::genesis::run(args).await, + Commands::Fixtures(cmd) => match cmd.command { + fixtures::Cmds::UpdateClient(args) => update_client::run(args).await, + fixtures::Cmds::Membership(args) => membership::run(args).await, + fixtures::Cmds::UpdateClientAndMembership(args) => uc_and_mem::run(args).await, + fixtures::Cmds::Misbehaviour(args) => misbehaviour::run(args).await, + }, + } +} diff --git a/programs/operator/src/cli/command.rs b/programs/operator/src/cli/command.rs new file mode 100644 index 000000000..49163e34 --- /dev/null +++ b/programs/operator/src/cli/command.rs @@ -0,0 +1,257 @@ +//! Contains the command line interface for the application. + +use std::convert::Infallible; + +use clap::{command, Parser}; +use sp1_ics07_tendermint_prover::prover::SupportedProofType; +use tendermint_light_client_verifier::types::TrustThreshold; + +/// The command line interface for the operator. +#[derive(Clone, Debug, Parser)] +#[command(author, version, about, long_about = None)] +pub struct OperatorCli { + /// The subcommand to run. + #[command(subcommand)] + pub command: Commands, +} + +/// The subcommands for the operator. +#[derive(Clone, Debug, Parser)] +pub enum Commands { + /// The subcommand to run the operator. + Start(operator::Args), + /// The subcommand to produce the `genesis.json` file. + Genesis(genesis::Args), + /// The subcommand to produce the fixtures for testing. + Fixtures(fixtures::Cmd), +} + +/// The trust options for client operations. +#[derive(Clone, Debug, Parser)] +pub struct TrustOptions { + /// Trust level. + #[clap( + long, + default_value = "1/3", + value_parser = parse_trust_threshold, + help = "Trust level as a fraction, e.g. '2/3'", + )] + pub trust_level: TrustThreshold, + + /// Trusting period. [default: 2/3 of unbonding period] + #[clap(long)] + pub trusting_period: Option, +} + +/// The output path for files. +#[derive(Debug, Clone)] +pub enum OutputPath { + /// Write the output to stdout. + Stdout, + /// Write the output to a file. + File(String), +} + +/// The cli interface for the genesis command. +pub mod genesis { + use super::Parser; + + /// The arguments for the `genesis` executable. + #[derive(Parser, Debug, Clone)] + pub struct Args { + /// Trusted block height. [default: latest] + #[clap(long)] + pub trusted_block: Option, + + /// Genesis path. If not provided, the output will be written to stdout. + #[clap(long, short = 'o', value_parser = super::parse_output_path, default_value = "-")] + pub output_path: super::OutputPath, + + /// Trust options + #[clap(flatten)] + pub trust_options: super::TrustOptions, + + /// The proof type + /// Supported proof types: groth16, plonk. + #[clap(long, short = 'p', value_parser = super::parse_proof_type, default_value = "plonk")] + pub proof_type: super::SupportedProofType, + } +} + +/// The cli interface for the operator. +pub mod operator { + use super::Parser; + + /// Command line arguments for the operator. + #[derive(Parser, Debug, Clone)] + pub struct Args { + /// Run update-client only once and then exit. + #[clap(long)] + pub only_once: bool, + } +} + +/// The cli interface for the fixtures. +pub mod fixtures { + use super::{command, Parser}; + + /// The cli interface for the fixtures. + #[derive(Clone, Debug, Parser)] + #[command(about = "Generate fixtures for SP1ICS07Tendermint contract")] + pub struct Cmd { + /// The subcommand to run. + #[command(subcommand)] + pub command: Cmds, + } + + /// The subcommands for the fixtures. + #[derive(Clone, Debug, Parser)] + pub enum Cmds { + /// The subcommand to generate the update client fixtures. + UpdateClient(UpdateClientCmd), + /// The subcommand to generate the verify (non)membership fixtures. + Membership(MembershipCmd), + /// The subcommand to generate the update client and verify (non)membership fixtures. + UpdateClientAndMembership(UpdateClientAndMembershipCmd), + /// The subcommand to generate the misbehaviour fixtures. + Misbehaviour(MisbehaviourCmd), + } + + /// The arguments for the `UpdateClient` fixture executable. + #[derive(Parser, Debug, Clone)] + #[command(about = "Generate the update client fixture")] + pub struct UpdateClientCmd { + /// Trusted block. + #[clap(long)] + pub trusted_block: u32, + + /// Target block. + #[clap(long, env)] + pub target_block: u32, + + /// Fixture path. If not provided, the output will be written to stdout. + #[clap(long, short = 'o', value_parser = super::parse_output_path, default_value = "-")] + pub output_path: super::OutputPath, + + /// Trust options + #[clap(flatten)] + pub trust_options: super::TrustOptions, + + /// The proof type + /// Supported proof types: groth16, plonk. + #[clap(long, short = 'p', value_parser = super::parse_proof_type, default_value = "plonk")] + pub proof_type: super::SupportedProofType, + } + + /// The arguments for the `Membership` fixture executable. + #[derive(Parser, Debug, Clone)] + #[command(about = "Generate the verify (non)membership fixture")] + pub struct MembershipCmd { + /// Generic membership arguments. + #[clap(flatten)] + pub membership: MembershipArgs, + + /// The proof type. + /// Supported proof types: groth16, plonk. + #[clap(long, short = 'p', value_parser = super::parse_proof_type, default_value = "plonk")] + pub proof_type: super::SupportedProofType, + } + + /// The arguments for generic membership proof generation. + #[derive(Parser, Debug, Clone)] + pub struct MembershipArgs { + /// Trusted block. + #[clap(long)] + pub trusted_block: u32, + + /// Key paths to prove membership. + #[clap(long, value_delimiter = ',')] + pub key_paths: Vec, + + /// Fixture path. If not provided, the output will be written to stdout. + #[clap(long, short = 'o', value_parser = super::parse_output_path, default_value = "-")] + pub output_path: super::OutputPath, + + /// Trust options + #[clap(flatten)] + pub trust_options: super::TrustOptions, + + /// Indicates that the key paths are base64 encoded. + /// Module store keys seperated by backslash, '\', eg. 'aWJj\a2V5' for 'ibc/key'. + #[clap(long)] + pub base64: bool, + } + + /// The arguments for the `UpdateClientAndMembership` fixture executable. + #[derive(Parser, Debug, Clone)] + #[command(about = "Generate the update client and membership fixture")] + pub struct UpdateClientAndMembershipCmd { + /// Target block. + #[clap(long, env)] + pub target_block: u32, + + /// Membership arguments. + #[clap(flatten)] + pub membership: MembershipArgs, + + /// The proof type + /// Supported proof types: groth16, plonk. + #[clap(long, short = 'p', value_parser = super::parse_proof_type, default_value = "plonk")] + pub proof_type: super::SupportedProofType, + } + + /// The arguments for the `Misbehaviour` fixture executable. + #[derive(Parser, Debug, Clone)] + #[command(about = "Generate the misbehaviour fixture")] + pub struct MisbehaviourCmd { + /// Path to the misbehaviour json file. + #[clap(long)] + pub misbehaviour_path: String, + + /// Fixture path. If not provided, the output will be written to stdout. + #[clap(long, short = 'o', value_parser = super::parse_output_path, default_value = "-")] + pub output_path: super::OutputPath, + + /// Trust options + #[clap(flatten)] + pub trust_options: super::TrustOptions, + + /// The proof type + /// Supported proof types: groth16, plonk. + #[clap(long, short = 'p', value_parser = super::parse_proof_type, default_value = "plonk")] + pub proof_type: super::SupportedProofType, + } +} + +#[allow(clippy::unnecessary_wraps)] +fn parse_output_path(path: &str) -> Result { + if path == "-" { + Ok(OutputPath::Stdout) + } else { + Ok(OutputPath::File(path.to_string())) + } +} + +fn parse_trust_threshold(input: &str) -> anyhow::Result { + let (num_part, denom_part) = input.split_once('/').ok_or_else(|| { + anyhow::anyhow!("invalid trust threshold fraction: expected format 'numerator/denominator'") + })?; + let numerator = num_part + .trim() + .parse() + .map_err(|_| anyhow::anyhow!("invalid numerator for the fraction"))?; + let denominator = denom_part + .trim() + .parse() + .map_err(|_| anyhow::anyhow!("invalid denominator for the fraction"))?; + TrustThreshold::new(numerator, denominator) + .map_err(|e| anyhow::anyhow!("invalid trust threshold: {}", e)) +} + +fn parse_proof_type(input: &str) -> anyhow::Result { + match input { + "groth16" => Ok(SupportedProofType::Groth16), + "plonk" => Ok(SupportedProofType::Plonk), + _ => Err(anyhow::anyhow!("invalid proof type")), + } +} diff --git a/programs/operator/src/cli/mod.rs b/programs/operator/src/cli/mod.rs new file mode 100644 index 000000000..ca6fa694 --- /dev/null +++ b/programs/operator/src/cli/mod.rs @@ -0,0 +1,3 @@ +//! Module for the cli interface + +pub mod command; diff --git a/programs/operator/src/lib.rs b/programs/operator/src/lib.rs new file mode 100644 index 000000000..ffa7b0e0 --- /dev/null +++ b/programs/operator/src/lib.rs @@ -0,0 +1,5 @@ +//! The crate that contains the types and utilities for `sp1-ics07-tendermint-operator` executable. +#![deny(missing_docs, clippy::nursery, clippy::pedantic, warnings)] + +pub mod cli; +pub mod runners; diff --git a/programs/operator/src/runners/fixtures/membership.rs b/programs/operator/src/runners/fixtures/membership.rs new file mode 100644 index 000000000..82db30d9 --- /dev/null +++ b/programs/operator/src/runners/fixtures/membership.rs @@ -0,0 +1,161 @@ +//! Runner for generating `membership` fixtures + +use crate::{ + cli::command::{fixtures::MembershipCmd, OutputPath}, + runners::genesis::SP1ICS07TendermintGenesis, +}; +use alloy_sol_types::SolValue; +use core::str; +use ibc_client_tendermint_types::ConsensusState; +use ibc_core_commitment_types::merkle::MerkleProof; +use ibc_eureka_solidity_types::sp1_ics07::{ + IICS07TendermintMsgs::{ClientState, ConsensusState as SolConsensusState}, + IMembershipMsgs::{MembershipOutput, MembershipProof, SP1MembershipProof}, + ISP1Msgs::SP1Proof, +}; +use serde::{Deserialize, Serialize}; +use sp1_ics07_tendermint_prover::{ + programs::MembershipProgram, + prover::{SP1ICS07TendermintProver, SupportedProofType}, +}; +use sp1_ics07_tendermint_utils::{merkle::convert_tm_to_ics_merkle_proof, rpc::TendermintRpcExt}; +use sp1_sdk::HashableKey; +use std::path::PathBuf; +use tendermint_rpc::{Client, HttpClient}; + +/// The fixture data to be used in [`MembershipProgram`] tests. +#[serde_with::serde_as] +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SP1ICS07MembershipFixture { + /// The genesis data. + #[serde(flatten)] + pub genesis: SP1ICS07TendermintGenesis, + /// The height of the proof. + #[serde_as(as = "serde_with::hex::Hex")] + pub proof_height: Vec, + /// The encoded public values. + #[serde_as(as = "serde_with::hex::Hex")] + pub membership_proof: Vec, +} + +/// Writes the proof data for the given trusted and target blocks to the given fixture path. +#[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] +pub async fn run(args: MembershipCmd) -> anyhow::Result<()> { + assert!(!args.membership.key_paths.is_empty()); + + let tm_rpc_client = HttpClient::from_env(); + + let trusted_light_block = tm_rpc_client + .get_light_block(Some(args.membership.trusted_block)) + .await?; + + let genesis = SP1ICS07TendermintGenesis::from_env( + &trusted_light_block, + args.membership.trust_options.trusting_period, + args.membership.trust_options.trust_level, + args.proof_type, + ) + .await?; + + let trusted_client_state = ClientState::abi_decode(&genesis.trusted_client_state, false)?; + let trusted_consensus_state = + SolConsensusState::abi_decode(&genesis.trusted_consensus_state, false)?; + + let membership_proof = run_sp1_membership( + &tm_rpc_client, + args.membership.base64, + args.membership.key_paths, + args.membership.trusted_block, + trusted_consensus_state, + args.proof_type, + ) + .await?; + + let fixture = SP1ICS07MembershipFixture { + genesis, + proof_height: trusted_client_state.latestHeight.abi_encode(), + membership_proof: membership_proof.abi_encode(), + }; + + match args.membership.output_path { + OutputPath::File(path) => { + // Save the proof data to the file path. + std::fs::write(PathBuf::from(path), serde_json::to_string_pretty(&fixture)?).unwrap(); + } + OutputPath::Stdout => { + println!("{}", serde_json::to_string_pretty(&fixture)?); + } + } + + Ok(()) +} + +/// Generates an sp1 membership proof for the given args +#[allow( + clippy::missing_errors_doc, + clippy::missing_panics_doc, + clippy::module_name_repetitions +)] +pub async fn run_sp1_membership( + tm_rpc_client: &HttpClient, + is_base64: bool, + key_paths: Vec, + trusted_block: u32, + trusted_consensus_state: SolConsensusState, + proof_type: SupportedProofType, +) -> anyhow::Result { + let verify_mem_prover = SP1ICS07TendermintProver::::new(proof_type); + let commitment_root_bytes = ConsensusState::from(trusted_consensus_state.clone()) + .root + .as_bytes() + .to_vec(); + + let kv_proofs: Vec<(Vec>, Vec, MerkleProof)> = + futures::future::try_join_all(key_paths.into_iter().map(|path| async { + let path: Vec> = if is_base64 { + path.split('\\') + .map(subtle_encoding::base64::decode) + .collect::>()? + } else { + vec![b"ibc".into(), path.into_bytes()] + }; + assert_eq!(path.len(), 2); + + let res = tm_rpc_client + .abci_query( + Some(format!("store/{}/key", str::from_utf8(&path[0])?)), + path[1].as_slice(), + // Proof height should be the block before the target block. + Some((trusted_block - 1).into()), + true, + ) + .await?; + + assert_eq!(u32::try_from(res.height.value())? + 1, trusted_block); + assert_eq!(res.key.as_slice(), path[1].as_slice()); + let vm_proof = convert_tm_to_ics_merkle_proof(&res.proof.unwrap())?; + assert!(!vm_proof.proofs.is_empty()); + + anyhow::Ok((path, res.value, vm_proof)) + })) + .await?; + + // Generate a header update proof for the specified blocks. + let proof_data = verify_mem_prover.generate_proof(&commitment_root_bytes, kv_proofs); + + let bytes = proof_data.public_values.as_slice(); + let output = MembershipOutput::abi_decode(bytes, true)?; + assert_eq!(output.commitmentRoot.as_slice(), &commitment_root_bytes); + + let sp1_membership_proof = SP1MembershipProof { + sp1Proof: SP1Proof::new( + &verify_mem_prover.vkey.bytes32(), + proof_data.bytes(), + proof_data.public_values.to_vec(), + ), + trustedConsensusState: trusted_consensus_state, + }; + + Ok(MembershipProof::from(sp1_membership_proof)) +} diff --git a/programs/operator/src/runners/fixtures/misbehaviour.rs b/programs/operator/src/runners/fixtures/misbehaviour.rs new file mode 100644 index 000000000..8054d324 --- /dev/null +++ b/programs/operator/src/runners/fixtures/misbehaviour.rs @@ -0,0 +1,141 @@ +//! Runner for generating `misbehaviour` fixtures + +use crate::{ + cli::command::{fixtures::MisbehaviourCmd, OutputPath}, + runners::genesis::SP1ICS07TendermintGenesis, +}; +use alloy_sol_types::SolValue; +use ibc_client_tendermint_types::Misbehaviour; +use ibc_eureka_solidity_types::sp1_ics07::{ + IICS07TendermintMsgs::{ClientState, ConsensusState}, + IMisbehaviourMsgs::MsgSubmitMisbehaviour, + ISP1Msgs::SP1Proof, +}; +use ibc_proto::ibc::lightclients::tendermint::v1::Misbehaviour as RawMisbehaviour; +use serde::{Deserialize, Serialize}; +use sp1_ics07_tendermint_prover::{ + programs::MisbehaviourProgram, prover::SP1ICS07TendermintProver, +}; +use sp1_ics07_tendermint_utils::rpc::TendermintRpcExt; +use sp1_sdk::HashableKey; +use std::path::PathBuf; +use tendermint_rpc::HttpClient; + +/// The fixture data to be used in [`SP1ICS07SubmitMisbehaviourFixture`] tests. +#[serde_with::serde_as] +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct SP1ICS07SubmitMisbehaviourFixture { + /// The genesis data. + #[serde(flatten)] + genesis: SP1ICS07TendermintGenesis, + + /// The encoded submit misbehaviour client message. + #[serde_as(as = "serde_with::hex::Hex")] + submit_msg: Vec, +} + +/// Writes the proof data for misbehaviour to the given fixture path. +#[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] +pub async fn run(args: MisbehaviourCmd) -> anyhow::Result<()> { + let path = args.misbehaviour_path; + let misbehaviour_bz = std::fs::read(path)?; + // deserialize from json + let raw_misbehaviour: RawMisbehaviour = serde_json::from_slice(&misbehaviour_bz)?; + + let tm_rpc_client = HttpClient::from_env(); + + // get light block for trusted height of header 1 + #[allow(clippy::cast_possible_truncation)] + let trusted_light_block_1 = tm_rpc_client + .get_light_block(Some( + raw_misbehaviour + .header_1 + .as_ref() + .unwrap() + .trusted_height + .unwrap() + .revision_height as u32, + )) + .await?; + #[allow(clippy::cast_possible_truncation)] + // get light block for trusted height of header 2 + let trusted_light_block_2 = tm_rpc_client + .get_light_block(Some( + raw_misbehaviour + .header_2 + .as_ref() + .unwrap() + .trusted_height + .unwrap() + .revision_height as u32, + )) + .await?; + + // use trusted light block 1 to instantiate a new SP1 tendermint client with light block 1 as initial trusted consensus state + let genesis_1 = SP1ICS07TendermintGenesis::from_env( + &trusted_light_block_1, + args.trust_options.trusting_period, + args.trust_options.trust_level, + args.proof_type, + ) + .await?; + // use trusted light block 2 to instantiate a new SP1 tendermint client with light block 2 as initial trusted consensus state + let genesis_2 = SP1ICS07TendermintGenesis::from_env( + &trusted_light_block_2, + args.trust_options.trusting_period, + args.trust_options.trust_level, + args.proof_type, + ) + .await?; + + // use the clients to convert the Tendermint light blocks into the IBC Tendermint trusted consensus states + let trusted_consensus_state_1 = + ConsensusState::abi_decode(&genesis_1.trusted_consensus_state, false)?; + let trusted_consensus_state_2 = + ConsensusState::abi_decode(&genesis_2.trusted_consensus_state, false)?; + + // use the client state from genesis_2 as the client state since they will both be the same + let trusted_client_state_2 = ClientState::abi_decode(&genesis_2.trusted_client_state, false)?; + + let verify_misbehaviour_prover = + SP1ICS07TendermintProver::::new(args.proof_type); + + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH)? + .as_secs(); + + let misbehaviour: Misbehaviour = Misbehaviour::try_from(raw_misbehaviour).unwrap(); + let proof_data = verify_misbehaviour_prover.generate_proof( + &trusted_client_state_2, + &misbehaviour, + &trusted_consensus_state_1, + &trusted_consensus_state_2, + now, + ); + + let submit_msg = MsgSubmitMisbehaviour { + sp1Proof: SP1Proof::new( + &verify_misbehaviour_prover.vkey.bytes32(), + proof_data.bytes(), + proof_data.public_values.to_vec(), + ), + }; + + let fixture = SP1ICS07SubmitMisbehaviourFixture { + genesis: genesis_2, + submit_msg: submit_msg.abi_encode(), + }; + + match args.output_path { + OutputPath::File(path) => { + // Save the proof data to the file path. + std::fs::write(PathBuf::from(path), serde_json::to_string_pretty(&fixture)?)?; + } + OutputPath::Stdout => { + println!("{}", serde_json::to_string_pretty(&fixture)?); + } + } + + Ok(()) +} diff --git a/programs/operator/src/runners/fixtures/mod.rs b/programs/operator/src/runners/fixtures/mod.rs new file mode 100644 index 000000000..898d44bd --- /dev/null +++ b/programs/operator/src/runners/fixtures/mod.rs @@ -0,0 +1,6 @@ +//! Runners for generating fixtures for testing of the programs. + +pub mod membership; +pub mod misbehaviour; +pub mod uc_and_mem; +pub mod update_client; diff --git a/programs/operator/src/runners/fixtures/uc_and_mem.rs b/programs/operator/src/runners/fixtures/uc_and_mem.rs new file mode 100644 index 000000000..0b6dd078 --- /dev/null +++ b/programs/operator/src/runners/fixtures/uc_and_mem.rs @@ -0,0 +1,132 @@ +//! Runner for generating `update_client` fixtures + +use crate::{ + cli::command::{fixtures::UpdateClientAndMembershipCmd, OutputPath}, + runners::{ + fixtures::membership::SP1ICS07MembershipFixture, genesis::SP1ICS07TendermintGenesis, + }, +}; +use alloy_sol_types::SolValue; +use core::str; +use ibc_client_tendermint_types::ConsensusState; +use ibc_core_commitment_types::merkle::MerkleProof; +use ibc_eureka_solidity_types::sp1_ics07::{ + IICS07TendermintMsgs::{ClientState, ConsensusState as SolConsensusState}, + IMembershipMsgs::{MembershipProof, SP1MembershipAndUpdateClientProof}, + ISP1Msgs::SP1Proof, + IUpdateClientAndMembershipMsgs::UcAndMembershipOutput, +}; +use sp1_ics07_tendermint_prover::{ + programs::UpdateClientAndMembershipProgram, prover::SP1ICS07TendermintProver, +}; +use sp1_ics07_tendermint_utils::merkle::convert_tm_to_ics_merkle_proof; +use sp1_ics07_tendermint_utils::{light_block::LightBlockExt, rpc::TendermintRpcExt}; +use sp1_sdk::HashableKey; +use std::path::PathBuf; +use tendermint_rpc::{Client, HttpClient}; + +/// Writes the proof data for the given trusted and target blocks to the given fixture path. +#[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] +pub async fn run(args: UpdateClientAndMembershipCmd) -> anyhow::Result<()> { + assert!( + args.membership.trusted_block < args.target_block, + "The target block must be greater than the trusted block" + ); + + let tm_rpc_client = HttpClient::from_env(); + let uc_mem_prover = + SP1ICS07TendermintProver::::new(args.proof_type); + + let trusted_light_block = tm_rpc_client + .get_light_block(Some(args.membership.trusted_block)) + .await?; + let target_light_block = tm_rpc_client + .get_light_block(Some(args.target_block)) + .await?; + + let genesis = SP1ICS07TendermintGenesis::from_env( + &trusted_light_block, + args.membership.trust_options.trusting_period, + args.membership.trust_options.trust_level, + args.proof_type, + ) + .await?; + let trusted_client_state = ClientState::abi_decode(&genesis.trusted_client_state, false)?; + let trusted_consensus_state: ConsensusState = + SolConsensusState::abi_decode(&genesis.trusted_consensus_state, false)?.into(); + + let proposed_header = target_light_block.into_header(&trusted_light_block); + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH)? + .as_secs(); + + let kv_proofs: Vec<(Vec>, Vec, MerkleProof)> = + futures::future::try_join_all(args.membership.key_paths.into_iter().map(|path| async { + let path: Vec> = if args.membership.base64 { + path.split('\\') + .map(subtle_encoding::base64::decode) + .collect::>()? + } else { + vec![b"ibc".into(), path.into_bytes()] + }; + assert_eq!(path.len(), 2); + + let res = tm_rpc_client + .abci_query( + Some(format!("store/{}/key", str::from_utf8(&path[0])?)), + path[1].as_slice(), + // Proof height should be the block before the target block. + Some((args.target_block - 1).into()), + true, + ) + .await?; + + assert_eq!(u32::try_from(res.height.value())? + 1, args.target_block); + assert_eq!(res.key.as_slice(), path[1].as_slice()); + let vm_proof = convert_tm_to_ics_merkle_proof(&res.proof.unwrap())?; + assert!(!vm_proof.proofs.is_empty()); + + anyhow::Ok((path, res.value, vm_proof)) + })) + .await?; + + let kv_len = kv_proofs.len(); + // Generate a header update proof for the specified blocks. + let proof_data = uc_mem_prover.generate_proof( + &trusted_client_state, + &trusted_consensus_state.into(), + &proposed_header, + now, + kv_proofs, + ); + + let bytes = proof_data.public_values.as_slice(); + let output = UcAndMembershipOutput::abi_decode(bytes, false)?; + assert_eq!(output.kvPairs.len(), kv_len); + + let sp1_membership_proof = SP1MembershipAndUpdateClientProof { + sp1Proof: SP1Proof::new( + &uc_mem_prover.vkey.bytes32(), + proof_data.bytes(), + proof_data.public_values.to_vec(), + ), + }; + + let fixture = SP1ICS07MembershipFixture { + genesis, + proof_height: output.updateClientOutput.newHeight.abi_encode(), + membership_proof: MembershipProof::from(sp1_membership_proof).abi_encode(), + }; + + match args.membership.output_path { + OutputPath::File(path) => { + // Save the proof data to the file path. + std::fs::write(PathBuf::from(path), serde_json::to_string_pretty(&fixture)?)?; + } + OutputPath::Stdout => { + println!("{}", serde_json::to_string_pretty(&fixture)?); + } + } + + Ok(()) +} diff --git a/programs/operator/src/runners/fixtures/update_client.rs b/programs/operator/src/runners/fixtures/update_client.rs new file mode 100644 index 000000000..dc2f016c --- /dev/null +++ b/programs/operator/src/runners/fixtures/update_client.rs @@ -0,0 +1,111 @@ +//! Runner for generating `update_client` fixtures + +use crate::{ + cli::command::{fixtures::UpdateClientCmd, OutputPath}, + runners::genesis::SP1ICS07TendermintGenesis, +}; +use alloy_sol_types::SolValue; +use ibc_eureka_solidity_types::sp1_ics07::{ + IICS07TendermintMsgs::{ClientState, ConsensusState}, + ISP1Msgs::SP1Proof, + IUpdateClientMsgs::{MsgUpdateClient, UpdateClientOutput}, +}; +use serde::{Deserialize, Serialize}; +use sp1_ics07_tendermint_prover::{ + programs::UpdateClientProgram, prover::SP1ICS07TendermintProver, +}; +use sp1_ics07_tendermint_utils::{light_block::LightBlockExt, rpc::TendermintRpcExt}; +use sp1_sdk::HashableKey; +use std::path::PathBuf; +use tendermint_rpc::HttpClient; + +/// The fixture data to be used in [`UpdateClientProgram`] tests. +#[serde_with::serde_as] +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct SP1ICS07UpdateClientFixture { + /// The genesis data. + #[serde(flatten)] + genesis: SP1ICS07TendermintGenesis, + /// The encoded target consensus state. + #[serde_as(as = "serde_with::hex::Hex")] + target_consensus_state: Vec, + /// Target height. + target_height: u32, + /// The encoded update client message. + #[serde_as(as = "serde_with::hex::Hex")] + update_msg: Vec, +} + +/// Writes the proof data for the given trusted and target blocks to the given fixture path. +#[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] +pub async fn run(args: UpdateClientCmd) -> anyhow::Result<()> { + assert!( + args.trusted_block < args.target_block, + "The target block must be greater than the trusted block" + ); + + let tm_rpc_client = HttpClient::from_env(); + let uc_prover = SP1ICS07TendermintProver::::new(args.proof_type); + + let trusted_light_block = tm_rpc_client + .get_light_block(Some(args.trusted_block)) + .await?; + let target_light_block = tm_rpc_client + .get_light_block(Some(args.target_block)) + .await?; + + let genesis = SP1ICS07TendermintGenesis::from_env( + &trusted_light_block, + args.trust_options.trusting_period, + args.trust_options.trust_level, + args.proof_type, + ) + .await?; + + let trusted_consensus_state = + ConsensusState::abi_decode(&genesis.trusted_consensus_state, false)?; + let trusted_client_state = ClientState::abi_decode(&genesis.trusted_client_state, false)?; + + let proposed_header = target_light_block.into_header(&trusted_light_block); + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH)? + .as_secs(); + + // Generate a header update proof for the specified blocks. + let proof_data = uc_prover.generate_proof( + &trusted_client_state, + &trusted_consensus_state, + &proposed_header, + now, + ); + + let output = UpdateClientOutput::abi_decode(proof_data.public_values.as_slice(), false)?; + + let update_msg = MsgUpdateClient { + sp1Proof: SP1Proof::new( + &uc_prover.vkey.bytes32(), + proof_data.bytes(), + proof_data.public_values.to_vec(), + ), + }; + + let fixture = SP1ICS07UpdateClientFixture { + genesis, + target_consensus_state: output.newConsensusState.abi_encode(), + target_height: args.target_block, + update_msg: update_msg.abi_encode(), + }; + + match args.output_path { + OutputPath::File(path) => { + // Save the proof data to the file path. + std::fs::write(PathBuf::from(path), serde_json::to_string_pretty(&fixture)?)?; + } + OutputPath::Stdout => { + println!("{}", serde_json::to_string_pretty(&fixture)?); + } + } + + Ok(()) +} diff --git a/programs/operator/src/runners/genesis.rs b/programs/operator/src/runners/genesis.rs new file mode 100644 index 000000000..102a8666 --- /dev/null +++ b/programs/operator/src/runners/genesis.rs @@ -0,0 +1,125 @@ +//! Contains the runner for the genesis command. + +use crate::cli::command::{genesis::Args, OutputPath}; +use alloy_sol_types::SolValue; +use ibc_eureka_solidity_types::sp1_ics07::IICS07TendermintMsgs::ConsensusState as SolConsensusState; +use sp1_ics07_tendermint_prover::{ + programs::{ + MembershipProgram, MisbehaviourProgram, SP1Program, UpdateClientAndMembershipProgram, + UpdateClientProgram, + }, + prover::SupportedProofType, +}; +use sp1_ics07_tendermint_utils::{light_block::LightBlockExt, rpc::TendermintRpcExt}; +use sp1_sdk::{utils::setup_logger, HashableKey}; +use std::path::PathBuf; +use tendermint_light_client_verifier::types::{LightBlock, TrustThreshold}; +use tendermint_rpc::HttpClient; + +/// The genesis data for the SP1 ICS07 Tendermint contract. +#[serde_with::serde_as] +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +#[allow(clippy::module_name_repetitions)] +pub struct SP1ICS07TendermintGenesis { + /// The encoded trusted client state. + #[serde_as(as = "serde_with::hex::Hex")] + pub trusted_client_state: Vec, + /// The encoded trusted consensus state. + #[serde_as(as = "serde_with::hex::Hex")] + pub trusted_consensus_state: Vec, + /// The encoded key for [`UpdateClientProgram`]. + update_client_vkey: String, + /// The encoded key for [`MembershipProgram`]. + membership_vkey: String, + /// The encoded key for [`UpdateClientAndMembershipProgram`]. + uc_and_membership_vkey: String, + /// The encoded key for [`MisbehaviourProgram`]. + misbehaviour_vkey: String, +} + +impl SP1ICS07TendermintGenesis { + /// Creates a new genesis instance by reading the environment variables + /// and making the necessary RPC calls. + #[allow(clippy::missing_errors_doc)] + pub async fn from_env( + trusted_light_block: &LightBlock, + trusting_period: Option, + trust_level: TrustThreshold, + proof_type: SupportedProofType, + ) -> anyhow::Result { + setup_logger(); + if dotenv::dotenv().is_err() { + tracing::warn!("No .env file found"); + } + + let tm_rpc_client = HttpClient::from_env(); + + let unbonding_period = tm_rpc_client + .sdk_staking_params() + .await? + .unbonding_time + .ok_or_else(|| anyhow::anyhow!("No unbonding time found"))? + .seconds + .try_into()?; + + // Defaults to the recommended TrustingPeriod: 2/3 of the UnbondingPeriod + let trusting_period = trusting_period.unwrap_or(2 * (unbonding_period / 3)); + if trusting_period > unbonding_period { + return Err(anyhow::anyhow!( + "Trusting period cannot be greater than unbonding period" + )); + } + + let trusted_client_state = trusted_light_block.to_sol_client_state( + trust_level.try_into()?, + unbonding_period, + trusting_period, + proof_type.into(), + )?; + let trusted_consensus_state = trusted_light_block.to_consensus_state(); + + Ok(Self { + trusted_consensus_state: SolConsensusState::from(trusted_consensus_state).abi_encode(), + trusted_client_state: trusted_client_state.abi_encode(), + update_client_vkey: UpdateClientProgram::get_vkey().bytes32(), + membership_vkey: MembershipProgram::get_vkey().bytes32(), + uc_and_membership_vkey: UpdateClientAndMembershipProgram::get_vkey().bytes32(), + misbehaviour_vkey: MisbehaviourProgram::get_vkey().bytes32(), + }) + } +} + +/// Creates the `genesis.json` file for the `SP1ICS07Tendermint` contract. +#[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] +pub async fn run(args: Args) -> anyhow::Result<()> { + let tm_rpc_client = HttpClient::from_env(); + + let trusted_light_block = tm_rpc_client.get_light_block(args.trusted_block).await?; + if args.trusted_block.is_none() { + tracing::info!( + "Latest block height: {}", + trusted_light_block.height().value() + ); + } + + let genesis = SP1ICS07TendermintGenesis::from_env( + &trusted_light_block, + args.trust_options.trusting_period, + args.trust_options.trust_level, + args.proof_type, + ) + .await?; + + match args.output_path { + OutputPath::File(path) => { + // Save the proof data to the file path. + std::fs::write(PathBuf::from(path), serde_json::to_string_pretty(&genesis)?)?; + } + OutputPath::Stdout => { + println!("{}", serde_json::to_string_pretty(&genesis)?); + } + } + + Ok(()) +} diff --git a/programs/operator/src/runners/mod.rs b/programs/operator/src/runners/mod.rs new file mode 100644 index 000000000..6b6e0623 --- /dev/null +++ b/programs/operator/src/runners/mod.rs @@ -0,0 +1,5 @@ +//! Contains the runners for the different types of commands. + +pub mod fixtures; +pub mod genesis; +pub mod operator; diff --git a/programs/operator/src/runners/operator.rs b/programs/operator/src/runners/operator.rs new file mode 100644 index 000000000..ac5a0b15 --- /dev/null +++ b/programs/operator/src/runners/operator.rs @@ -0,0 +1,113 @@ +//! Contains the runner for the `operator run` command. + +use std::env; + +use crate::cli::command::operator::Args; +use alloy::providers::ProviderBuilder; +use alloy_sol_types::SolValue; +use anyhow::anyhow; +use ibc_eureka_solidity_types::sp1_ics07::{ + sp1_ics07_tendermint, ISP1Msgs::SP1Proof, IUpdateClientMsgs::MsgUpdateClient, +}; +use reqwest::Url; +use sp1_ics07_tendermint_prover::{ + programs::UpdateClientProgram, + prover::{SP1ICS07TendermintProver, SupportedProofType}, +}; +use sp1_ics07_tendermint_utils::{eth, light_block::LightBlockExt, rpc::TendermintRpcExt}; +use sp1_sdk::{utils::setup_logger, HashableKey}; +use tendermint_rpc::HttpClient; + +/// Runs the update client program in a loop. +/// If the `only_once` flag is set, the program will only run once. +#[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] +pub async fn run(args: Args) -> anyhow::Result<()> { + setup_logger(); + if dotenv::dotenv().is_err() { + tracing::warn!("No .env file found"); + } + + let rpc_url = env::var("RPC_URL").expect("RPC_URL not set"); + let contract_address = env::var("CONTRACT_ADDRESS").expect("CONTRACT_ADDRESS not set"); + + // Instantiate a Tendermint prover based on the environment variable. + let wallet = eth::wallet_from_env(); + let provider = ProviderBuilder::new() + .with_recommended_fillers() + .wallet(wallet) + .on_http(Url::parse(rpc_url.as_str())?); + + let contract = sp1_ics07_tendermint::new(contract_address.parse()?, provider); + let contract_client_state = contract.getClientState().call().await?._0; + let tendermint_rpc_client = HttpClient::from_env(); + let prover = SP1ICS07TendermintProver::::new( + SupportedProofType::try_from(contract_client_state.zkAlgorithm).map_err(|e| anyhow!(e))?, + ); + + loop { + let contract_client_state = contract.getClientState().call().await?._0; + + // Read the existing trusted header hash from the contract. + let trusted_block_height = contract_client_state.latestHeight.revisionHeight; + assert!( + trusted_block_height != 0, + "No trusted height found on the contract. Something is wrong with the contract." + ); + + let trusted_light_block = tendermint_rpc_client + .get_light_block(Some(trusted_block_height)) + .await?; + + // Get trusted consensus state from the trusted light block. + let trusted_consensus_state = trusted_light_block.to_consensus_state().into(); + + let target_light_block = tendermint_rpc_client.get_light_block(None).await?; + let target_height = target_light_block.height().value(); + + // Get the proposed header from the target light block. + let proposed_header = target_light_block.into_header(&trusted_light_block); + + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH)? + .as_secs(); + + // Generate a proof of the transition from the trusted block to the target block. + let proof_data = prover.generate_proof( + &contract_client_state, + &trusted_consensus_state, + &proposed_header, + now, + ); + + let update_msg = MsgUpdateClient { + sp1Proof: SP1Proof::new( + &prover.vkey.bytes32(), + proof_data.bytes(), + proof_data.public_values.to_vec(), + ), + }; + + contract + .updateClient(update_msg.abi_encode().into()) + .send() + .await? + .watch() + .await?; + + tracing::info!( + "Updated the ICS-07 Tendermint light client at address {} from block {} to block {}.", + contract_address, + trusted_block_height, + target_height + ); + + if args.only_once { + tracing::info!("Exiting because '--only-once' flag is set."); + return Ok(()); + } + + // Sleep for 60 seconds. + tracing::debug!("sleeping for 60 seconds"); + tokio::time::sleep(std::time::Duration::from_secs(60)).await; + } +} diff --git a/relayer/Cargo.toml b/programs/relayer/Cargo.toml similarity index 100% rename from relayer/Cargo.toml rename to programs/relayer/Cargo.toml diff --git a/relayer/README.md b/programs/relayer/README.md similarity index 100% rename from relayer/README.md rename to programs/relayer/README.md diff --git a/relayer/build.rs b/programs/relayer/build.rs similarity index 100% rename from relayer/build.rs rename to programs/relayer/build.rs diff --git a/relayer/config.example.json b/programs/relayer/config.example.json similarity index 100% rename from relayer/config.example.json rename to programs/relayer/config.example.json diff --git a/relayer/proto/relayer/relayer.proto b/programs/relayer/proto/relayer/relayer.proto similarity index 100% rename from relayer/proto/relayer/relayer.proto rename to programs/relayer/proto/relayer/relayer.proto diff --git a/relayer/src/bin/relayer.rs b/programs/relayer/src/bin/relayer.rs similarity index 100% rename from relayer/src/bin/relayer.rs rename to programs/relayer/src/bin/relayer.rs diff --git a/relayer/src/cli/cmd.rs b/programs/relayer/src/cli/cmd.rs similarity index 100% rename from relayer/src/cli/cmd.rs rename to programs/relayer/src/cli/cmd.rs diff --git a/relayer/src/cli/config.rs b/programs/relayer/src/cli/config.rs similarity index 100% rename from relayer/src/cli/config.rs rename to programs/relayer/src/cli/config.rs diff --git a/relayer/src/cli/mod.rs b/programs/relayer/src/cli/mod.rs similarity index 100% rename from relayer/src/cli/mod.rs rename to programs/relayer/src/cli/mod.rs diff --git a/relayer/src/core/builder.rs b/programs/relayer/src/core/builder.rs similarity index 100% rename from relayer/src/core/builder.rs rename to programs/relayer/src/core/builder.rs diff --git a/relayer/src/core/mod.rs b/programs/relayer/src/core/mod.rs similarity index 100% rename from relayer/src/core/mod.rs rename to programs/relayer/src/core/mod.rs diff --git a/relayer/src/core/modules.rs b/programs/relayer/src/core/modules.rs similarity index 100% rename from relayer/src/core/modules.rs rename to programs/relayer/src/core/modules.rs diff --git a/relayer/src/lib.rs b/programs/relayer/src/lib.rs similarity index 100% rename from relayer/src/lib.rs rename to programs/relayer/src/lib.rs diff --git a/relayer/src/modules/cosmos_to_eth.rs b/programs/relayer/src/modules/cosmos_to_eth.rs similarity index 100% rename from relayer/src/modules/cosmos_to_eth.rs rename to programs/relayer/src/modules/cosmos_to_eth.rs diff --git a/relayer/src/modules/mod.rs b/programs/relayer/src/modules/mod.rs similarity index 100% rename from relayer/src/modules/mod.rs rename to programs/relayer/src/modules/mod.rs diff --git a/programs/sp1-programs/membership/Cargo.toml b/programs/sp1-programs/membership/Cargo.toml new file mode 100644 index 000000000..8702ce83 --- /dev/null +++ b/programs/sp1-programs/membership/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "sp1-ics07-tendermint-membership" +description = "Verify (non)membership program for sp1-ics07-tendermint" +version = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +license = { workspace = true } + +[[bin]] +name = "sp1-ics07-tendermint-membership" +test = false + +[dependencies] +sp1-zkvm = { workspace = true, default-features = true } +ibc-core-commitment-types = { workspace = true } +alloy-sol-types = { workspace = true } +ibc-eureka-solidity-types = { workspace = true } +ibc-proto = { workspace = true } +bincode = { workspace = true } diff --git a/programs/sp1-programs/membership/src/lib.rs b/programs/sp1-programs/membership/src/lib.rs new file mode 100644 index 000000000..fcaaa94a --- /dev/null +++ b/programs/sp1-programs/membership/src/lib.rs @@ -0,0 +1,63 @@ +//! The crate that contains the types and utilities for `sp1-ics07-tendermint-membership` program. +#![deny(missing_docs, clippy::nursery, clippy::pedantic, warnings)] + +use ibc_eureka_solidity_types::sp1_ics07::IMembershipMsgs::{KVPair, MembershipOutput}; + +use ibc_core_commitment_types::{ + commitment::CommitmentRoot, + merkle::{MerklePath, MerkleProof}, + proto::ics23::HostFunctionsManager, + specs::ProofSpecs, +}; + +/// The main function of the program without the zkVM wrapper. +#[allow(clippy::missing_panics_doc)] +#[must_use] +pub fn membership( + app_hash: [u8; 32], + request_iter: impl Iterator>, Vec, MerkleProof)>, +) -> MembershipOutput { + let commitment_root = CommitmentRoot::from_bytes(&app_hash); + + let kv_pairs = request_iter + .map(|(path, value, merkle_proof)| { + let merkle_path = MerklePath { + key_path: path.into_iter().map(Into::into).collect(), + }; + + if value.is_empty() { + merkle_proof + .verify_non_membership::( + &ProofSpecs::cosmos(), + commitment_root.clone().into(), + merkle_path.clone(), + ) + .unwrap(); + } else { + merkle_proof + .verify_membership::( + &ProofSpecs::cosmos(), + commitment_root.clone().into(), + merkle_path.clone(), + value.clone(), + 0, + ) + .unwrap(); + } + + KVPair { + path: merkle_path + .key_path + .into_iter() + .map(|v| v.into_vec().into()) + .collect(), + value: value.into(), + } + }) + .collect(); + + MembershipOutput { + commitmentRoot: app_hash.into(), + kvPairs: kv_pairs, + } +} diff --git a/programs/sp1-programs/membership/src/main.rs b/programs/sp1-programs/membership/src/main.rs new file mode 100644 index 000000000..f4c68704 --- /dev/null +++ b/programs/sp1-programs/membership/src/main.rs @@ -0,0 +1,49 @@ +//! A program that verifies the membership or non-membership of a value in a commitment root. + +#![deny(missing_docs, clippy::nursery, clippy::pedantic, warnings)] +#![allow(clippy::no_mangle_with_rust_abi)] +// These two lines are necessary for the program to properly compile. +// +// Under the hood, we wrap your main function with some extra code so that it behaves properly +// inside the zkVM. +#![no_main] +sp1_zkvm::entrypoint!(main); + +use alloy_sol_types::SolValue; + +use ibc_proto::Protobuf; +use sp1_ics07_tendermint_membership::membership; + +use ibc_core_commitment_types::merkle::MerkleProof; + +/// The main function of the program. +/// +/// # Panics +/// Panics if the verification fails. +pub fn main() { + let encoded_1 = sp1_zkvm::io::read_vec(); + let app_hash: [u8; 32] = encoded_1.try_into().unwrap(); + + // encoded_2 is the number of key-value pairs we want to verify + let request_len = sp1_zkvm::io::read_vec()[0]; + assert!(request_len != 0); + + let request_iter = (0..request_len).map(|_| { + // loop_encoded_1 is the path we want to verify the membership of + let loop_encoded_1 = sp1_zkvm::io::read_vec(); + let path = bincode::deserialize(&loop_encoded_1).unwrap(); + + // loop_encoded_2 is the value we want to prove the membership of + // if it is empty, we are verifying non-membership + let value = sp1_zkvm::io::read_vec(); + + let loop_encoded_3 = sp1_zkvm::io::read_vec(); + let merkle_proof = MerkleProof::decode_vec(&loop_encoded_3).unwrap(); + + (path, value, merkle_proof) + }); + + let output = membership(app_hash, request_iter); + + sp1_zkvm::io::commit_slice(&output.abi_encode()); +} diff --git a/programs/sp1-programs/misbehaviour/Cargo.toml b/programs/sp1-programs/misbehaviour/Cargo.toml new file mode 100644 index 000000000..41e0658f --- /dev/null +++ b/programs/sp1-programs/misbehaviour/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "sp1-ics07-tendermint-misbehaviour" +description = "Check for misbehaviour program for sp1-ics07-tendermint" +version = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +license = { workspace = true } + +[[bin]] +name = "sp1-ics07-tendermint-misbehaviour" +test = false + +[dependencies] +sp1-zkvm = { workspace = true, default-features = true } +ibc-eureka-solidity-types = { workspace = true } +tendermint-light-client-verifier = { workspace = true } +ibc-client-tendermint = { workspace = true, features = ["serde"] } +ibc-core-host-types = { workspace = true } +ibc-core-client = { workspace = true } +ibc-primitives = { workspace = true } +ibc-core-handler-types = { workspace = true } +sha2 = { workspace = true } +alloy-sol-types = { workspace = true } +serde_cbor = { workspace = true, features = ["std"] } +bincode = { workspace = true } diff --git a/programs/sp1-programs/misbehaviour/src/lib.rs b/programs/sp1-programs/misbehaviour/src/lib.rs new file mode 100644 index 000000000..0fe6b9c4 --- /dev/null +++ b/programs/sp1-programs/misbehaviour/src/lib.rs @@ -0,0 +1,91 @@ +//! The crate that contains the types and utilities for `sp1-ics07-tendermint-update-client` +//! program. +#![deny(missing_docs, clippy::nursery, clippy::pedantic, warnings)] + +pub mod types; + +use ibc_client_tendermint::client_state::{ + check_for_misbehaviour_on_misbehavior, verify_misbehaviour, +}; +use ibc_client_tendermint::types::{ConsensusState, Misbehaviour, TENDERMINT_CLIENT_TYPE}; +use ibc_core_host_types::identifiers::{ChainId, ClientId}; +use ibc_eureka_solidity_types::sp1_ics07::{ + IICS07TendermintMsgs::ClientState, IMisbehaviourMsgs::MisbehaviourOutput, +}; +use std::collections::HashMap; +use std::time::Duration; +use tendermint_light_client_verifier::options::Options; +use tendermint_light_client_verifier::ProdVerifier; + +/// The main function of the program without the zkVM wrapper. +#[allow(clippy::missing_panics_doc)] +#[must_use] +pub fn check_for_misbehaviour( + client_state: ClientState, + misbehaviour: &Misbehaviour, + trusted_consensus_state_1: ConsensusState, + trusted_consensus_state_2: ConsensusState, + time: u64, +) -> MisbehaviourOutput { + let client_id = ClientId::new(TENDERMINT_CLIENT_TYPE, 0).unwrap(); + assert_eq!( + client_state.chainId, + misbehaviour + .header1() + .signed_header + .header + .chain_id + .to_string() + ); + + // Insert the two trusted consensus states into the trusted consensus state map that exists in the ClientValidationContext that is expected by verifyMisbehaviour + // Since we are mocking the existence of prior trusted consensus states, we are only filling in the two consensus states that are passed in into the map + let trusted_consensus_state_map = HashMap::from([ + ( + misbehaviour.header1().trusted_height.revision_height(), + &trusted_consensus_state_1, + ), + ( + misbehaviour.header2().trusted_height.revision_height(), + &trusted_consensus_state_2, + ), + ]); + let ctx = + types::validation::MisbehaviourValidationContext::new(time, trusted_consensus_state_map); + + let options = Options { + trust_threshold: client_state.trustLevel.clone().into(), + trusting_period: Duration::from_secs(client_state.trustingPeriod.into()), + clock_drift: Duration::default(), + }; + + // Call into ibc-rs verify_misbehaviour function to verify that both headers are valid given their respective trusted consensus states + verify_misbehaviour::<_, sha2::Sha256>( + &ctx, + misbehaviour, + &client_id, + &ChainId::new(&client_state.chainId).unwrap(), + &options, + &ProdVerifier::default(), + ) + .unwrap(); + + // Call into ibc-rs check_for_misbehaviour_on_misbehaviour method to ensure that the misbehaviour is valid + // i.e. the headers are same height but different commits, or headers are not monotonically increasing in time + let is_misbehaviour = + check_for_misbehaviour_on_misbehavior(misbehaviour.header1(), misbehaviour.header2()) + .unwrap(); + assert!(is_misbehaviour, "Misbehaviour is not detected"); + + // The prover takes in the trusted headers as an input but does not maintain its own internal state + // Thus, the verifier must ensure that the trusted headers that were used in the proof are trusted consensus + // states stored in its own internal state before it can accept the misbehaviour proof as valid. + MisbehaviourOutput { + clientState: client_state, + trustedHeight1: misbehaviour.header1().trusted_height.try_into().unwrap(), + trustedHeight2: misbehaviour.header2().trusted_height.try_into().unwrap(), + trustedConsensusState1: trusted_consensus_state_1.into(), + trustedConsensusState2: trusted_consensus_state_2.into(), + time, + } +} diff --git a/programs/sp1-programs/misbehaviour/src/main.rs b/programs/sp1-programs/misbehaviour/src/main.rs new file mode 100644 index 000000000..f6740378 --- /dev/null +++ b/programs/sp1-programs/misbehaviour/src/main.rs @@ -0,0 +1,54 @@ +//! A program that verifies a misbehaviour evidence. + +#![deny(missing_docs, clippy::nursery, clippy::pedantic, warnings)] +#![allow(clippy::no_mangle_with_rust_abi)] +// These two lines are necessary for the program to properly compile. +// +// Under the hood, we wrap your main function with some extra code so that it behaves properly +// inside the zkVM. +#![no_main] +sp1_zkvm::entrypoint!(main); + +use alloy_sol_types::SolValue; +use ibc_client_tendermint::types::Misbehaviour; +use ibc_eureka_solidity_types::sp1_ics07::IICS07TendermintMsgs::{ + ClientState as SolClientState, ConsensusState as SolConsensusState, +}; +use sp1_ics07_tendermint_misbehaviour::check_for_misbehaviour; + +/// The main function of the program. +/// +/// # Panics +/// Panics if the verification fails. +pub fn main() { + let encoded_1 = sp1_zkvm::io::read_vec(); + let encoded_2 = sp1_zkvm::io::read_vec(); + let encoded_3 = sp1_zkvm::io::read_vec(); + let encoded_4 = sp1_zkvm::io::read_vec(); + let encoded_5 = sp1_zkvm::io::read_vec(); + + // input 1: client state + let client_state = bincode::deserialize::(&encoded_1).unwrap(); + // input 2: the misbehaviour evidence + let misbehaviour = serde_cbor::from_slice::(&encoded_2).unwrap(); + // input 3: header 1 trusted consensus state + let trusted_consensus_state_1 = bincode::deserialize::(&encoded_3) + .unwrap() + .into(); + // input 4: header 2 trusted consensus state + let trusted_consensus_state_2 = bincode::deserialize::(&encoded_4) + .unwrap() + .into(); + // input 5: time + let time = u64::from_le_bytes(encoded_5.try_into().unwrap()); + + let output = check_for_misbehaviour( + client_state, + &misbehaviour, + trusted_consensus_state_1, + trusted_consensus_state_2, + time, + ); + + sp1_zkvm::io::commit_slice(&output.abi_encode()); +} diff --git a/programs/sp1-programs/misbehaviour/src/types/mod.rs b/programs/sp1-programs/misbehaviour/src/types/mod.rs new file mode 100644 index 000000000..c63405d4 --- /dev/null +++ b/programs/sp1-programs/misbehaviour/src/types/mod.rs @@ -0,0 +1,3 @@ +//! Containes types used in the program. + +pub mod validation; diff --git a/programs/sp1-programs/misbehaviour/src/types/validation.rs b/programs/sp1-programs/misbehaviour/src/types/validation.rs new file mode 100644 index 000000000..682641dc --- /dev/null +++ b/programs/sp1-programs/misbehaviour/src/types/validation.rs @@ -0,0 +1,100 @@ +//! Contains types and traits for `verify_misbehaviour` validation within the program. + +use ibc_client_tendermint::{ + client_state::ClientState as ClientStateWrapper, + consensus_state::ConsensusState as ConsensusStateWrapper, types::ConsensusState, +}; +use ibc_core_client::context::{ClientValidationContext, ExtClientValidationContext}; +use ibc_core_host_types::error::HostError; +use ibc_primitives::Timestamp; +use std::collections::HashMap; + +/// The client validation context. +pub struct MisbehaviourValidationContext<'a> { + /// Current time in seconds. + time: u64, + trusted_consensus_states: HashMap, +} + +impl<'a> MisbehaviourValidationContext<'a> { + /// Create a new instance of the client validation context. + #[must_use] + pub const fn new( + time: u64, + trusted_consensus_states: HashMap, + ) -> Self { + Self { + time, + trusted_consensus_states, + } + } +} + +impl ClientValidationContext for MisbehaviourValidationContext<'_> { + type ClientStateRef = ClientStateWrapper; + type ConsensusStateRef = ConsensusStateWrapper; + + fn consensus_state( + &self, + client_cons_state_path: &ibc_core_host_types::path::ClientConsensusStatePath, + ) -> Result { + let height = client_cons_state_path.revision_height; + let trusted_consensus_state = self.trusted_consensus_states[&height]; + + Ok(trusted_consensus_state.clone().into()) + } + + fn client_state( + &self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + ) -> Result { + // not needed by the `verify_misbehaviour` function + unimplemented!() + } + + fn client_update_meta( + &self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + _height: &ibc_core_client::types::Height, + ) -> Result<(Timestamp, ibc_core_client::types::Height), HostError> { + // not needed by the `verify_misbehaviour` function + unimplemented!() + } +} + +impl ExtClientValidationContext for MisbehaviourValidationContext<'_> { + fn host_timestamp(&self) -> Result { + Ok(Timestamp::from_nanoseconds(self.time * 1_000_000_000)) + } + + fn host_height(&self) -> Result { + // not needed by the `verify_misbehaviour` function + unimplemented!() + } + + fn consensus_state_heights( + &self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + ) -> Result, HostError> { + // not needed by the `verify_misbehaviour` function + unimplemented!() + } + + fn next_consensus_state( + &self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + _height: &ibc_core_client::types::Height, + ) -> Result, HostError> { + // not needed by the `verify_misbehaviour` function + unimplemented!() + } + + fn prev_consensus_state( + &self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + _height: &ibc_core_client::types::Height, + ) -> Result, HostError> { + // not needed by the `verify_misbehaviour` function + unimplemented!() + } +} diff --git a/programs/sp1-programs/uc-and-membership/Cargo.toml b/programs/sp1-programs/uc-and-membership/Cargo.toml new file mode 100644 index 000000000..23b6d4a9 --- /dev/null +++ b/programs/sp1-programs/uc-and-membership/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "sp1-ics07-tendermint-uc-and-membership" +description = "Update client and verify (non)membership program for sp1-ics07-tendermint" +version = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +license = { workspace = true } + +[[bin]] +name = "sp1-ics07-tendermint-uc-and-membership" +test = false + +[dependencies] +sp1-zkvm = { workspace = true, default-features = true } +ibc-core-commitment-types = { workspace = true } +ibc-client-tendermint-types = { workspace = true } +alloy-sol-types = { workspace = true } +ibc-eureka-solidity-types = { workspace = true } +sp1-ics07-tendermint-update-client = { workspace = true } +sp1-ics07-tendermint-membership = { workspace = true } +ibc-proto = { workspace = true } +serde_cbor = { workspace = true, features = ["std"] } +bincode = { workspace = true } diff --git a/programs/sp1-programs/uc-and-membership/src/lib.rs b/programs/sp1-programs/uc-and-membership/src/lib.rs new file mode 100644 index 000000000..543b5923 --- /dev/null +++ b/programs/sp1-programs/uc-and-membership/src/lib.rs @@ -0,0 +1,43 @@ +//! The crate that contains the types and utilities for `sp1-ics07-tendermint-membership` program. +#![deny(missing_docs, clippy::nursery, clippy::pedantic, warnings)] + +use ibc_eureka_solidity_types::sp1_ics07::{ + IICS07TendermintMsgs::ClientState, IUpdateClientAndMembershipMsgs::UcAndMembershipOutput, +}; + +use ibc_client_tendermint_types::{ConsensusState, Header}; + +use ibc_core_commitment_types::merkle::MerkleProof; + +/// The main function of the program without the zkVM wrapper. +#[allow(clippy::missing_panics_doc)] +#[must_use] +pub fn update_client_and_membership( + client_state: ClientState, + trusted_consensus_state: ConsensusState, + proposed_header: Header, + time: u64, + request_iter: impl Iterator>, Vec, MerkleProof)>, +) -> UcAndMembershipOutput { + let app_hash: [u8; 32] = proposed_header + .signed_header + .header() + .app_hash + .as_bytes() + .try_into() + .unwrap(); + + let uc_output = sp1_ics07_tendermint_update_client::update_client( + client_state, + trusted_consensus_state, + proposed_header, + time, + ); + + let mem_output = sp1_ics07_tendermint_membership::membership(app_hash, request_iter); + + UcAndMembershipOutput { + updateClientOutput: uc_output, + kvPairs: mem_output.kvPairs, + } +} diff --git a/programs/sp1-programs/uc-and-membership/src/main.rs b/programs/sp1-programs/uc-and-membership/src/main.rs new file mode 100644 index 000000000..ad0d965f --- /dev/null +++ b/programs/sp1-programs/uc-and-membership/src/main.rs @@ -0,0 +1,73 @@ +//! A program that verifies the membership or non-membership of a value in a commitment root. + +#![deny(missing_docs, clippy::nursery, clippy::pedantic, warnings)] +#![allow(clippy::no_mangle_with_rust_abi)] +// These two lines are necessary for the program to properly compile. +// +// Under the hood, we wrap your main function with some extra code so that it behaves properly +// inside the zkVM. +#![no_main] +sp1_zkvm::entrypoint!(main); + +use alloy_sol_types::SolValue; + +use ibc_proto::Protobuf; +use sp1_ics07_tendermint_uc_and_membership::update_client_and_membership; + +use ibc_core_commitment_types::merkle::MerkleProof; + +use ibc_client_tendermint_types::Header; +use ibc_eureka_solidity_types::sp1_ics07::IICS07TendermintMsgs::{ + ClientState as SolClientState, ConsensusState as SolConsensusState, +}; + +/// The main function of the program. +/// +/// # Panics +/// Panics if the verification fails. +pub fn main() { + let encoded_1 = sp1_zkvm::io::read_vec(); + let encoded_2 = sp1_zkvm::io::read_vec(); + let encoded_3 = sp1_zkvm::io::read_vec(); + let encoded_4 = sp1_zkvm::io::read_vec(); + // encoded_5 is the number of key-value pairs we want to verify + let request_len = sp1_zkvm::io::read_vec()[0]; + assert!(request_len != 0); + + // input 1: the client state + let client_state = bincode::deserialize::(&encoded_1).unwrap(); + // input 2: the trusted consensus state + let trusted_consensus_state = bincode::deserialize::(&encoded_2) + .unwrap() + .into(); + // input 3: the proposed header + let proposed_header = serde_cbor::from_slice::
(&encoded_3).unwrap(); + // input 4: time + let time = u64::from_le_bytes(encoded_4.try_into().unwrap()); + // TODO: find an encoding that works for all the structs above. + + let request_iter = (0..request_len).map(|_| { + // loop_encoded_1 is the path we want to verify the membership of + let loop_encoded_1 = sp1_zkvm::io::read_vec(); + let path = bincode::deserialize(&loop_encoded_1).unwrap(); + + // loop_encoded_2 is the value we want to prove the membership of + // if it is empty, we are verifying non-membership + let value = sp1_zkvm::io::read_vec(); + + let loop_encoded_3 = sp1_zkvm::io::read_vec(); + let merkle_proof = MerkleProof::decode_vec(&loop_encoded_3).unwrap(); + + (path, value, merkle_proof) + }); + + let output = update_client_and_membership( + client_state, + trusted_consensus_state, + proposed_header, + time, + request_iter, + ); + + sp1_zkvm::io::commit_slice(&output.abi_encode()); +} diff --git a/programs/sp1-programs/update-client/Cargo.toml b/programs/sp1-programs/update-client/Cargo.toml new file mode 100644 index 000000000..00bdd181 --- /dev/null +++ b/programs/sp1-programs/update-client/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "sp1-ics07-tendermint-update-client" +description = "Update client program for sp1-ics07-tendermint" +version = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +license = { workspace = true } + +[[bin]] +name = "sp1-ics07-tendermint-update-client" +test = false + +[dependencies] +sp1-zkvm = { workspace = true, default-features = true } +ibc-eureka-solidity-types = { workspace = true } +tendermint-light-client-verifier = { workspace = true } +ibc-client-tendermint = { workspace = true, features = ["serde"] } +ibc-core-host-types = { workspace = true } +ibc-core-client = { workspace = true } +ibc-primitives = { workspace = true } +ibc-core-handler-types = { workspace = true } +sha2 = { workspace = true } +alloy-sol-types = { workspace = true } +serde_cbor = { workspace = true, features = ["std"] } +bincode = { workspace = true } diff --git a/programs/sp1-programs/update-client/src/lib.rs b/programs/sp1-programs/update-client/src/lib.rs new file mode 100644 index 000000000..170d12bf --- /dev/null +++ b/programs/sp1-programs/update-client/src/lib.rs @@ -0,0 +1,61 @@ +//! The crate that contains the types and utilities for `sp1-ics07-tendermint-update-client` +//! program. +#![deny(missing_docs, clippy::nursery, clippy::pedantic, warnings)] + +pub mod types; + +use std::{str::FromStr, time::Duration}; + +use ibc_client_tendermint::{ + client_state::verify_header, + types::{ConsensusState, Header, TENDERMINT_CLIENT_TYPE}, +}; +use ibc_core_host_types::identifiers::{ChainId, ClientId}; +use ibc_eureka_solidity_types::sp1_ics07::{ + IICS07TendermintMsgs::ClientState, IUpdateClientMsgs::UpdateClientOutput, +}; + +use tendermint_light_client_verifier::{options::Options, ProdVerifier}; + +/// The main function of the program without the zkVM wrapper. +#[allow(clippy::missing_panics_doc)] +#[must_use] +pub fn update_client( + client_state: ClientState, + trusted_consensus_state: ConsensusState, + proposed_header: Header, + time: u64, +) -> UpdateClientOutput { + let client_id = ClientId::new(TENDERMINT_CLIENT_TYPE, 0).unwrap(); + let chain_id = ChainId::from_str(&client_state.chainId).unwrap(); + let options = Options { + trust_threshold: client_state.trustLevel.clone().into(), + trusting_period: Duration::from_secs(client_state.trustingPeriod.into()), + clock_drift: Duration::default(), + }; + + let ctx = types::validation::ClientValidationCtx::new(time, &trusted_consensus_state); + + verify_header::<_, sha2::Sha256>( + &ctx, + &proposed_header, + &client_id, + &chain_id, + &options, + &ProdVerifier::default(), + ) + .unwrap(); + + let trusted_height = proposed_header.trusted_height.try_into().unwrap(); + let new_height = proposed_header.height().try_into().unwrap(); + let new_consensus_state = ConsensusState::from(proposed_header); + + UpdateClientOutput { + clientState: client_state, + trustedConsensusState: trusted_consensus_state.into(), + newConsensusState: new_consensus_state.into(), + time, + trustedHeight: trusted_height, + newHeight: new_height, + } +} diff --git a/programs/sp1-programs/update-client/src/main.rs b/programs/sp1-programs/update-client/src/main.rs new file mode 100644 index 000000000..da5f1c05 --- /dev/null +++ b/programs/sp1-programs/update-client/src/main.rs @@ -0,0 +1,46 @@ +//! A program that verifies the next block header of a blockchain using an IBC tendermint light +//! client. + +#![deny(missing_docs)] +#![deny(clippy::nursery, clippy::pedantic, warnings)] +#![allow(clippy::no_mangle_with_rust_abi)] +// These two lines are necessary for the program to properly compile. +// +// Under the hood, we wrap your main function with some extra code so that it behaves properly +// inside the zkVM. +#![no_main] +sp1_zkvm::entrypoint!(main); + +use alloy_sol_types::SolValue; +use ibc_client_tendermint::types::Header; +use ibc_eureka_solidity_types::sp1_ics07::IICS07TendermintMsgs::{ + ClientState as SolClientState, ConsensusState as SolConsensusState, +}; +use sp1_ics07_tendermint_update_client::update_client; + +/// The main function of the program. +/// +/// # Panics +/// Panics if the verification fails. +pub fn main() { + let encoded_1 = sp1_zkvm::io::read_vec(); + let encoded_2 = sp1_zkvm::io::read_vec(); + let encoded_3 = sp1_zkvm::io::read_vec(); + let encoded_4 = sp1_zkvm::io::read_vec(); + + // input 1: the client state + let client_state = bincode::deserialize::(&encoded_1).unwrap(); + // input 2: the trusted consensus state + let trusted_consensus_state = bincode::deserialize::(&encoded_2) + .unwrap() + .into(); + // input 3: the proposed header + let proposed_header = serde_cbor::from_slice::
(&encoded_3).unwrap(); + // input 4: time + let time = u64::from_le_bytes(encoded_4.try_into().unwrap()); + // TODO: find an encoding that works for all the structs above. + + let output = update_client(client_state, trusted_consensus_state, proposed_header, time); + + sp1_zkvm::io::commit_slice(&output.abi_encode()); +} diff --git a/programs/sp1-programs/update-client/src/types/mod.rs b/programs/sp1-programs/update-client/src/types/mod.rs new file mode 100644 index 000000000..c63405d4 --- /dev/null +++ b/programs/sp1-programs/update-client/src/types/mod.rs @@ -0,0 +1,3 @@ +//! Containes types used in the program. + +pub mod validation; diff --git a/programs/sp1-programs/update-client/src/types/validation.rs b/programs/sp1-programs/update-client/src/types/validation.rs new file mode 100644 index 000000000..83e59a90 --- /dev/null +++ b/programs/sp1-programs/update-client/src/types/validation.rs @@ -0,0 +1,95 @@ +//! Contains types and traits for `verify_header` validation within the program. + +use ibc_client_tendermint::{ + client_state::ClientState as ClientStateWrapper, + consensus_state::ConsensusState as ConsensusStateWrapper, types::ConsensusState, +}; +use ibc_core_client::context::{ClientValidationContext, ExtClientValidationContext}; +use ibc_core_host_types::error::HostError; +use ibc_primitives::Timestamp; + +/// The client validation context. +pub struct ClientValidationCtx<'a> { + /// Current time in seconds. + now: u64, + trusted_consensus_state: &'a ConsensusState, +} + +impl<'a> ClientValidationCtx<'a> { + /// Create a new instance of the client validation context. + #[must_use] + pub const fn new(now: u64, trusted_consensus_state: &'a ConsensusState) -> Self { + Self { + now, + trusted_consensus_state, + } + } +} + +impl ClientValidationContext for ClientValidationCtx<'_> { + type ClientStateRef = ClientStateWrapper; + type ConsensusStateRef = ConsensusStateWrapper; + + fn consensus_state( + &self, + _client_cons_state_path: &ibc_core_host_types::path::ClientConsensusStatePath, + ) -> Result { + // This is the trusted consensus state, whether or not it corresponds to the + // consensus state path will be checked in solidity. + Ok(self.trusted_consensus_state.clone().into()) + } + + fn client_state( + &self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + ) -> Result { + // not needed by the `verify_header` function + unimplemented!() + } + + fn client_update_meta( + &self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + _height: &ibc_core_client::types::Height, + ) -> Result<(Timestamp, ibc_core_client::types::Height), HostError> { + // not needed by the `verify_header` function + unimplemented!() + } +} + +impl ExtClientValidationContext for ClientValidationCtx<'_> { + fn host_timestamp(&self) -> Result { + Ok(Timestamp::from_nanoseconds(self.now * 1_000_000_000)) + } + + fn host_height(&self) -> Result { + // not needed by the `verify_header` function + unimplemented!() + } + + fn consensus_state_heights( + &self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + ) -> Result, HostError> { + // not needed by the `verify_header` function + unimplemented!() + } + + fn next_consensus_state( + &self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + _height: &ibc_core_client::types::Height, + ) -> Result, HostError> { + // not needed by the `verify_header` function + unimplemented!() + } + + fn prev_consensus_state( + &self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + _height: &ibc_core_client::types::Height, + ) -> Result, HostError> { + // not needed by the `verify_header` function + unimplemented!() + } +} diff --git a/remappings.txt b/remappings.txt index 4eca024c..9369c2cf 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,6 +1,3 @@ forge-std/=node_modules/forge-std/src/ @openzeppelin/=node_modules/@openzeppelin/contracts/ -@cosmos/sp1-ics07-tendermint/=node_modules/@cosmos/sp1-ics07-tendermint/contracts/src/ -solidity-ibc/=contracts/ @sp1-contracts/=node_modules/sp1-contracts/contracts/src/ -union-lib/=node_modules/union/evm/contracts/lib/ diff --git a/scripts/Base.s.sol b/scripts/Base.s.sol deleted file mode 100644 index 1bba672b..000000000 --- a/scripts/Base.s.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.28; - -import { Script } from "forge-std/Script.sol"; - -abstract contract BaseScript is Script { - /// @dev Included to enable compilation of the script without a $MNEMONIC environment variable. - string internal constant TEST_MNEMONIC = "test test test test test test test test test test test junk"; - - /// @dev Needed for the deterministic deployments. - bytes32 internal constant ZERO_SALT = bytes32(0); - - /// @dev The address of the transaction broadcaster. - address internal broadcaster; - - /// @dev Used to derive the broadcaster's address if $ETH_FROM is not defined. - string internal mnemonic; - - /// @dev Initializes the transaction broadcaster like this: - /// - /// - If $ETH_FROM is defined, use it. - /// - Otherwise, derive the broadcaster address from $MNEMONIC. - /// - If $MNEMONIC is not defined, default to a test mnemonic. - /// - /// The use case for $ETH_FROM is to specify the broadcaster key and its address via the command line. - constructor() { - address from = vm.envOr({ name: "ETH_FROM", defaultValue: address(0) }); - if (from != address(0)) { - broadcaster = from; - } else { - mnemonic = vm.envOr({ name: "MNEMONIC", defaultValue: TEST_MNEMONIC }); - (broadcaster,) = deriveRememberKey({ mnemonic: mnemonic, index: 0 }); - } - } - - modifier broadcast() { - vm.startBroadcast(broadcaster); - _; - vm.stopBroadcast(); - } -} diff --git a/scripts/Deploy.s.sol b/scripts/Deploy.s.sol deleted file mode 100644 index a15156cc..000000000 --- a/scripts/Deploy.s.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.28; - -import { BaseScript } from "./Base.s.sol"; - -/// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting -contract Deploy is BaseScript { - // solhint-disable-next-line no-empty-blocks - function run() public broadcast { } -} diff --git a/scripts/E2ETestDeploy.s.sol b/scripts/E2ETestDeploy.s.sol index c09ec5b8..5a2fba68 100644 --- a/scripts/E2ETestDeploy.s.sol +++ b/scripts/E2ETestDeploy.s.sol @@ -9,12 +9,11 @@ pragma solidity ^0.8.28; import { stdJson } from "forge-std/StdJson.sol"; import { Script } from "forge-std/Script.sol"; -import { SP1ICS07Tendermint } from "@cosmos/sp1-ics07-tendermint/SP1ICS07Tendermint.sol"; -import { IICS07TendermintMsgs } from "@cosmos/sp1-ics07-tendermint/msgs/IICS07TendermintMsgs.sol"; -import { ICS02Client } from "../contracts/ICS02Client.sol"; +import { SP1ICS07Tendermint } from "../contracts/light-clients/SP1ICS07Tendermint.sol"; +import { IICS07TendermintMsgs } from "../contracts/light-clients/msgs/IICS07TendermintMsgs.sol"; import { ICS26Router } from "../contracts/ICS26Router.sol"; import { ICS20Transfer } from "../contracts/ICS20Transfer.sol"; -import { TestERC20 } from "../test/mocks/TestERC20.sol"; +import { TestERC20 } from "../test/solidity-ibc/mocks/TestERC20.sol"; import { Strings } from "@openzeppelin/utils/Strings.sol"; import { ICS20Lib } from "../contracts/utils/ICS20Lib.sol"; @@ -31,6 +30,8 @@ struct SP1ICS07TendermintGenesisJson { contract E2ETestDeploy is Script { using stdJson for string; + string internal constant SP1_GENESIS_DIR = "/scripts/"; + function run() public returns (string memory) { // Read the initialization parameters for the SP1 Tendermint contract. SP1ICS07TendermintGenesisJson memory genesis = loadGenesis("genesis.json"); @@ -38,9 +39,8 @@ contract E2ETestDeploy is Script { abi.decode(genesis.trustedConsensusState, (IICS07TendermintMsgs.ConsensusState)); string memory e2eFaucet = vm.envString("E2E_FAUCET_ADDRESS"); - uint256 privateKey = vm.envUint("PRIVATE_KEY"); - vm.startBroadcast(privateKey); + vm.startBroadcast(); // Deploy the SP1 ICS07 Tendermint light client SP1ICS07Tendermint ics07Tendermint = new SP1ICS07Tendermint( @@ -82,7 +82,7 @@ contract E2ETestDeploy is Script { function loadGenesis(string memory fileName) public view returns (SP1ICS07TendermintGenesisJson memory) { string memory root = vm.projectRoot(); - string memory path = string.concat(root, "/e2e/", fileName); + string memory path = string.concat(root, SP1_GENESIS_DIR, fileName); string memory json = vm.readFile(path); bytes memory trustedClientState = json.readBytes(".trustedClientState"); bytes memory trustedConsensusState = json.readBytes(".trustedConsensusState"); diff --git a/scripts/SP1ICS07Tendermint.s.sol b/scripts/SP1ICS07Tendermint.s.sol new file mode 100644 index 000000000..5320a6f8 --- /dev/null +++ b/scripts/SP1ICS07Tendermint.s.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import { Script } from "forge-std/Script.sol"; +import { stdJson } from "forge-std/StdJson.sol"; +import { SP1ICS07Tendermint } from "../contracts/light-clients/SP1ICS07Tendermint.sol"; +import { IICS07TendermintMsgs } from "../contracts/light-clients/msgs/IICS07TendermintMsgs.sol"; + +struct SP1ICS07TendermintGenesisJson { + bytes trustedClientState; + bytes trustedConsensusState; + bytes32 updateClientVkey; + bytes32 membershipVkey; + bytes32 ucAndMembershipVkey; + bytes32 misbehaviourVkey; +} + +contract SP1TendermintScript is Script, IICS07TendermintMsgs { + using stdJson for string; + + SP1ICS07Tendermint public ics07Tendermint; + + string internal constant SP1_GENESIS_DIR = "/scripts/"; + + // Deploy the SP1 Tendermint contract with the supplied initialization parameters. + function run() public returns (address) { + // Read the initialization parameters for the SP1 Tendermint contract. + SP1ICS07TendermintGenesisJson memory genesis = loadGenesis("genesis.json"); + + ConsensusState memory trustedConsensusState = abi.decode(genesis.trustedConsensusState, (ConsensusState)); + + bytes32 trustedConsensusHash = keccak256(abi.encode(trustedConsensusState)); + + vm.startBroadcast(); + + ics07Tendermint = new SP1ICS07Tendermint( + genesis.updateClientVkey, + genesis.membershipVkey, + genesis.ucAndMembershipVkey, + genesis.misbehaviourVkey, + genesis.trustedClientState, + trustedConsensusHash + ); + + vm.stopBroadcast(); + + ClientState memory clientState = ics07Tendermint.getClientState(); + assert(keccak256(abi.encode(clientState)) == keccak256(genesis.trustedClientState)); + + bytes32 consensusHash = ics07Tendermint.getConsensusStateHash(clientState.latestHeight.revisionHeight); + assert(consensusHash == keccak256(abi.encode(trustedConsensusState))); + + return address(ics07Tendermint); + } + + function loadGenesis(string memory fileName) public view returns (SP1ICS07TendermintGenesisJson memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, SP1_GENESIS_DIR, fileName); + string memory json = vm.readFile(path); + bytes memory trustedClientState = json.readBytes(".trustedClientState"); + bytes memory trustedConsensusState = json.readBytes(".trustedConsensusState"); + bytes32 updateClientVkey = json.readBytes32(".updateClientVkey"); + bytes32 membershipVkey = json.readBytes32(".membershipVkey"); + bytes32 ucAndMembershipVkey = json.readBytes32(".ucAndMembershipVkey"); + bytes32 misbehaviourVkey = json.readBytes32(".misbehaviourVkey"); + + SP1ICS07TendermintGenesisJson memory fixture = SP1ICS07TendermintGenesisJson({ + trustedClientState: trustedClientState, + trustedConsensusState: trustedConsensusState, + updateClientVkey: updateClientVkey, + membershipVkey: membershipVkey, + ucAndMembershipVkey: ucAndMembershipVkey, + misbehaviourVkey: misbehaviourVkey + }); + + return fixture; + } +} diff --git a/test/BenchmarkTest.t.sol b/test/solidity-ibc/BenchmarkTest.t.sol similarity index 95% rename from test/BenchmarkTest.t.sol rename to test/solidity-ibc/BenchmarkTest.t.sol index 73b76ecf..3ff4e498 100644 --- a/test/BenchmarkTest.t.sol +++ b/test/solidity-ibc/BenchmarkTest.t.sol @@ -4,11 +4,11 @@ pragma solidity ^0.8.28; // solhint-disable custom-errors,max-line-length,avoid-low-level-calls import { TestERC20 } from "./mocks/TestERC20.sol"; -import { IICS20TransferMsgs } from "../contracts/msgs/IICS20TransferMsgs.sol"; -import { ICS20Lib } from "../contracts/utils/ICS20Lib.sol"; -import { ICS24Host } from "../contracts/utils/ICS24Host.sol"; +import { IICS20TransferMsgs } from "../../contracts/msgs/IICS20TransferMsgs.sol"; +import { ICS20Lib } from "../../contracts/utils/ICS20Lib.sol"; +import { ICS24Host } from "../../contracts/utils/ICS24Host.sol"; import { FixtureTest } from "./FixtureTest.t.sol"; -import { IICS26RouterMsgs } from "../contracts/msgs/IICS26RouterMsgs.sol"; +import { IICS26RouterMsgs } from "../../contracts/msgs/IICS26RouterMsgs.sol"; contract BenchmarkTest is FixtureTest { function test_ICS20TransferWithSP1Fixtures_Plonk() public { diff --git a/test/FixtureTest.t.sol b/test/solidity-ibc/FixtureTest.t.sol similarity index 83% rename from test/FixtureTest.t.sol rename to test/solidity-ibc/FixtureTest.t.sol index c38eb2c6..4996aa9a 100644 --- a/test/FixtureTest.t.sol +++ b/test/solidity-ibc/FixtureTest.t.sol @@ -4,14 +4,14 @@ pragma solidity ^0.8.28; // solhint-disable custom-errors,max-line-length import { Test } from "forge-std/Test.sol"; -import { IICS26RouterMsgs } from "../contracts/msgs/IICS26RouterMsgs.sol"; -import { IICS02ClientMsgs } from "../contracts/msgs/IICS02ClientMsgs.sol"; -import { ICS26Router } from "../contracts/ICS26Router.sol"; -import { IICS26RouterMsgs } from "../contracts/msgs/IICS26RouterMsgs.sol"; -import { SP1ICS07Tendermint } from "@cosmos/sp1-ics07-tendermint/SP1ICS07Tendermint.sol"; -import { ICS20Transfer } from "../contracts/ICS20Transfer.sol"; -import { IICS07TendermintMsgs } from "@cosmos/sp1-ics07-tendermint/msgs/IICS07TendermintMsgs.sol"; -import { ICS20Lib } from "../contracts/utils/ICS20Lib.sol"; +import { IICS26RouterMsgs } from "../../contracts/msgs/IICS26RouterMsgs.sol"; +import { IICS02ClientMsgs } from "../../contracts/msgs/IICS02ClientMsgs.sol"; +import { ICS26Router } from "../../contracts/ICS26Router.sol"; +import { IICS26RouterMsgs } from "../../contracts/msgs/IICS26RouterMsgs.sol"; +import { SP1ICS07Tendermint } from "../../contracts/light-clients/SP1ICS07Tendermint.sol"; +import { ICS20Transfer } from "../../contracts/ICS20Transfer.sol"; +import { IICS07TendermintMsgs } from "../../contracts/light-clients/msgs/IICS07TendermintMsgs.sol"; +import { ICS20Lib } from "../../contracts/utils/ICS20Lib.sol"; import { stdJson } from "forge-std/StdJson.sol"; abstract contract FixtureTest is Test { @@ -23,6 +23,8 @@ abstract contract FixtureTest is Test { bytes[] public merklePrefix = [bytes("ibc"), bytes("")]; bytes[] public singleSuccessAck = [ICS20Lib.SUCCESSFUL_ACKNOWLEDGEMENT_JSON]; + string internal constant FIXTURE_DIR = "/test/solidity-ibc/fixtures/"; + using stdJson for string; struct SP1ICS07GenesisFixtureJson { @@ -79,7 +81,7 @@ abstract contract FixtureTest is Test { function loadFixture(string memory fixtureFileName) internal returns (Fixture memory) { string memory root = vm.projectRoot(); - string memory path = string.concat(root, "/test/fixtures/", fixtureFileName); + string memory path = string.concat(root, FIXTURE_DIR, fixtureFileName); string memory json = vm.readFile(path); bytes memory sp1GenesisBz = json.readBytes(".sp1_genesis_fixture"); diff --git a/test/IBCERC20Test.t.sol b/test/solidity-ibc/IBCERC20Test.t.sol similarity index 94% rename from test/IBCERC20Test.t.sol rename to test/solidity-ibc/IBCERC20Test.t.sol index e86366b2..6a718f5d 100644 --- a/test/IBCERC20Test.t.sol +++ b/test/solidity-ibc/IBCERC20Test.t.sol @@ -4,12 +4,12 @@ pragma solidity ^0.8.28; // solhint-disable custom-errors,max-line-length import { Test } from "forge-std/Test.sol"; -import { IBCERC20 } from "../contracts/utils/IBCERC20.sol"; -import { IICS20Transfer } from "../contracts/interfaces/IICS20Transfer.sol"; +import { IBCERC20 } from "../../contracts/utils/IBCERC20.sol"; +import { IICS20Transfer } from "../../contracts/interfaces/IICS20Transfer.sol"; import { Ownable } from "@openzeppelin/access/Ownable.sol"; import { IERC20Errors } from "@openzeppelin/interfaces/draft-IERC6093.sol"; -import { Escrow } from "../contracts/utils/Escrow.sol"; -import { IICS26RouterMsgs } from "../contracts/msgs/IICS26RouterMsgs.sol"; +import { Escrow } from "../../contracts/utils/Escrow.sol"; +import { IICS26RouterMsgs } from "../../contracts/msgs/IICS26RouterMsgs.sol"; contract IBCERC20Test is Test, IICS20Transfer { IBCERC20 public ibcERC20; diff --git a/test/ICS02ClientTest.t.sol b/test/solidity-ibc/ICS02ClientTest.t.sol similarity index 81% rename from test/ICS02ClientTest.t.sol rename to test/solidity-ibc/ICS02ClientTest.t.sol index c6280e59..fe0f01eb 100644 --- a/test/ICS02ClientTest.t.sol +++ b/test/solidity-ibc/ICS02ClientTest.t.sol @@ -4,11 +4,11 @@ pragma solidity ^0.8.28; // solhint-disable custom-errors,max-line-length import { Test } from "forge-std/Test.sol"; -import { IICS02Client } from "../contracts/interfaces/IICS02Client.sol"; -import { ICS02Client } from "../contracts/ICS02Client.sol"; -import { IICS02ClientMsgs } from "../contracts/msgs/IICS02ClientMsgs.sol"; -import { ILightClient } from "../contracts/interfaces/ILightClient.sol"; -import { ILightClientMsgs } from "../contracts/msgs/ILightClientMsgs.sol"; +import { IICS02Client } from "../../contracts/interfaces/IICS02Client.sol"; +import { ICS02Client } from "../../contracts/ICS02Client.sol"; +import { IICS02ClientMsgs } from "../../contracts/msgs/IICS02ClientMsgs.sol"; +import { ILightClient } from "../../contracts/interfaces/ILightClient.sol"; +import { ILightClientMsgs } from "../../contracts/msgs/ILightClientMsgs.sol"; import { DummyLightClient } from "./mocks/DummyLightClient.sol"; contract ICS02ClientTest is Test { diff --git a/test/ICS20LibTest.t.sol b/test/solidity-ibc/ICS20LibTest.t.sol similarity index 95% rename from test/ICS20LibTest.t.sol rename to test/solidity-ibc/ICS20LibTest.t.sol index 99fe59f7..41d70478 100644 --- a/test/ICS20LibTest.t.sol +++ b/test/solidity-ibc/ICS20LibTest.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.28; // solhint-disable custom-errors,max-line-length import { Test } from "forge-std/Test.sol"; -import { ICS20Lib } from "../contracts/utils/ICS20Lib.sol"; +import { ICS20Lib } from "../../contracts/utils/ICS20Lib.sol"; contract ICS20LibTest is Test { struct IBCDenomTestCase { diff --git a/test/ICS20TransferTest.t.sol b/test/solidity-ibc/ICS20TransferTest.t.sol similarity index 98% rename from test/ICS20TransferTest.t.sol rename to test/solidity-ibc/ICS20TransferTest.t.sol index cf87014f..f8772399 100644 --- a/test/ICS20TransferTest.t.sol +++ b/test/solidity-ibc/ICS20TransferTest.t.sol @@ -4,17 +4,17 @@ pragma solidity ^0.8.28; // solhint-disable custom-errors,max-line-length import { Test } from "forge-std/Test.sol"; -import { IICS26RouterMsgs } from "../contracts/msgs/IICS26RouterMsgs.sol"; -import { IICS26Router } from "../contracts/interfaces/IICS26Router.sol"; -import { IIBCAppCallbacks } from "../contracts/msgs/IIBCAppCallbacks.sol"; -import { IICS20Transfer } from "../contracts/interfaces/IICS20Transfer.sol"; -import { IICS20TransferMsgs } from "../contracts/msgs/IICS20TransferMsgs.sol"; -import { ICS20Transfer } from "../contracts/ICS20Transfer.sol"; +import { IICS26RouterMsgs } from "../../contracts/msgs/IICS26RouterMsgs.sol"; +import { IICS26Router } from "../../contracts/interfaces/IICS26Router.sol"; +import { IIBCAppCallbacks } from "../../contracts/msgs/IIBCAppCallbacks.sol"; +import { IICS20Transfer } from "../../contracts/interfaces/IICS20Transfer.sol"; +import { IICS20TransferMsgs } from "../../contracts/msgs/IICS20TransferMsgs.sol"; +import { ICS20Transfer } from "../../contracts/ICS20Transfer.sol"; import { TestERC20, MalfunctioningERC20 } from "./mocks/TestERC20.sol"; -import { IBCERC20 } from "../contracts/utils/IBCERC20.sol"; +import { IBCERC20 } from "../../contracts/utils/IBCERC20.sol"; import { IERC20Errors } from "@openzeppelin/interfaces/draft-IERC6093.sol"; -import { ICS20Lib } from "../contracts/utils/ICS20Lib.sol"; -import { IICS20Errors } from "../contracts/errors/IICS20Errors.sol"; +import { ICS20Lib } from "../../contracts/utils/ICS20Lib.sol"; +import { IICS20Errors } from "../../contracts/errors/IICS20Errors.sol"; import { Strings } from "@openzeppelin/utils/Strings.sol"; import { Vm } from "forge-std/Vm.sol"; import { Ownable } from "@openzeppelin/access/Ownable.sol"; diff --git a/test/ICS24HostTest.t.sol b/test/solidity-ibc/ICS24HostTest.t.sol similarity index 96% rename from test/ICS24HostTest.t.sol rename to test/solidity-ibc/ICS24HostTest.t.sol index c587f077..598cc2be 100644 --- a/test/ICS24HostTest.t.sol +++ b/test/solidity-ibc/ICS24HostTest.t.sol @@ -4,9 +4,9 @@ pragma solidity ^0.8.28; // solhint-disable custom-errors,max-line-length import { Test } from "forge-std/Test.sol"; -import { ICS24Host } from "../contracts/utils/ICS24Host.sol"; -import { IICS26RouterMsgs } from "../contracts/msgs/IICS26RouterMsgs.sol"; -import { ICS20Lib } from "../contracts/utils/ICS20Lib.sol"; +import { ICS24Host } from "../../contracts/utils/ICS24Host.sol"; +import { IICS26RouterMsgs } from "../../contracts/msgs/IICS26RouterMsgs.sol"; +import { ICS20Lib } from "../../contracts/utils/ICS20Lib.sol"; import { Strings } from "@openzeppelin/utils/Strings.sol"; contract ICS24HostTest is Test { diff --git a/test/ICS26RouterTest.t.sol b/test/solidity-ibc/ICS26RouterTest.t.sol similarity index 85% rename from test/ICS26RouterTest.t.sol rename to test/solidity-ibc/ICS26RouterTest.t.sol index fcff9cf4..5a2ffba8 100644 --- a/test/ICS26RouterTest.t.sol +++ b/test/solidity-ibc/ICS26RouterTest.t.sol @@ -4,15 +4,15 @@ pragma solidity ^0.8.28; // solhint-disable custom-errors,max-line-length import { Test } from "forge-std/Test.sol"; -import { IICS02ClientMsgs } from "../contracts/msgs/IICS02ClientMsgs.sol"; -import { ICS26Router } from "../contracts/ICS26Router.sol"; -import { IICS26Router } from "../contracts/interfaces/IICS26Router.sol"; -import { IICS26RouterMsgs } from "../contracts/msgs/IICS26RouterMsgs.sol"; -import { ICS20Transfer } from "../contracts/ICS20Transfer.sol"; -import { ICS20Lib } from "../contracts/utils/ICS20Lib.sol"; +import { IICS02ClientMsgs } from "../../contracts/msgs/IICS02ClientMsgs.sol"; +import { ICS26Router } from "../../contracts/ICS26Router.sol"; +import { IICS26Router } from "../../contracts/interfaces/IICS26Router.sol"; +import { IICS26RouterMsgs } from "../../contracts/msgs/IICS26RouterMsgs.sol"; +import { ICS20Transfer } from "../../contracts/ICS20Transfer.sol"; +import { ICS20Lib } from "../../contracts/utils/ICS20Lib.sol"; import { Strings } from "@openzeppelin/utils/Strings.sol"; import { DummyLightClient } from "./mocks/DummyLightClient.sol"; -import { ILightClientMsgs } from "../contracts/msgs/ILightClientMsgs.sol"; +import { ILightClientMsgs } from "../../contracts/msgs/ILightClientMsgs.sol"; contract ICS26RouterTest is Test { ICS26Router public ics26Router; diff --git a/test/IbcIdentifiersTest.t.sol b/test/solidity-ibc/IbcIdentifiersTest.t.sol similarity index 97% rename from test/IbcIdentifiersTest.t.sol rename to test/solidity-ibc/IbcIdentifiersTest.t.sol index fabd2ee9..818d5f28 100644 --- a/test/IbcIdentifiersTest.t.sol +++ b/test/solidity-ibc/IbcIdentifiersTest.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.28; // solhint-disable max-line-length import { Test } from "forge-std/Test.sol"; -import { IBCIdentifiers } from "../contracts/utils/IBCIdentifiers.sol"; +import { IBCIdentifiers } from "../../contracts/utils/IBCIdentifiers.sol"; contract IBCIdentifiersTest is Test { struct ValidatePortIdentifierTestCase { diff --git a/test/IntegrationTest.t.sol b/test/solidity-ibc/IntegrationTest.t.sol similarity index 98% rename from test/IntegrationTest.t.sol rename to test/solidity-ibc/IntegrationTest.t.sol index 63ae45e2..d3f065b9 100644 --- a/test/IntegrationTest.t.sol +++ b/test/solidity-ibc/IntegrationTest.t.sol @@ -4,22 +4,22 @@ pragma solidity ^0.8.28; // solhint-disable custom-errors,max-line-length,max-states-count import { Test } from "forge-std/Test.sol"; -import { IICS02ClientMsgs } from "../contracts/msgs/IICS02ClientMsgs.sol"; -import { ICS20Transfer } from "../contracts/ICS20Transfer.sol"; -import { IICS20Transfer } from "../contracts/interfaces/IICS20Transfer.sol"; -import { IICS20TransferMsgs } from "../contracts/msgs/IICS20TransferMsgs.sol"; +import { IICS02ClientMsgs } from "../../contracts/msgs/IICS02ClientMsgs.sol"; +import { ICS20Transfer } from "../../contracts/ICS20Transfer.sol"; +import { IICS20Transfer } from "../../contracts/interfaces/IICS20Transfer.sol"; +import { IICS20TransferMsgs } from "../../contracts/msgs/IICS20TransferMsgs.sol"; import { TestERC20 } from "./mocks/TestERC20.sol"; -import { IBCERC20 } from "../contracts/utils/IBCERC20.sol"; -import { IICS26Router } from "../contracts/interfaces/IICS26Router.sol"; -import { IIBCStore } from "../contracts/interfaces/IIBCStore.sol"; -import { IICS26RouterErrors } from "../contracts/errors/IICS26RouterErrors.sol"; -import { ICS26Router } from "../contracts/ICS26Router.sol"; -import { IICS26RouterMsgs } from "../contracts/msgs/IICS26RouterMsgs.sol"; +import { IBCERC20 } from "../../contracts/utils/IBCERC20.sol"; +import { IICS26Router } from "../../contracts/interfaces/IICS26Router.sol"; +import { IIBCStore } from "../../contracts/interfaces/IIBCStore.sol"; +import { IICS26RouterErrors } from "../../contracts/errors/IICS26RouterErrors.sol"; +import { ICS26Router } from "../../contracts/ICS26Router.sol"; +import { IICS26RouterMsgs } from "../../contracts/msgs/IICS26RouterMsgs.sol"; import { DummyLightClient } from "./mocks/DummyLightClient.sol"; import { ErroneousIBCStore } from "./mocks/ErroneousIBCStore.sol"; -import { ILightClientMsgs } from "../contracts/msgs/ILightClientMsgs.sol"; -import { ICS20Lib } from "../contracts/utils/ICS20Lib.sol"; -import { ICS24Host } from "../contracts/utils/ICS24Host.sol"; +import { ILightClientMsgs } from "../../contracts/msgs/ILightClientMsgs.sol"; +import { ICS20Lib } from "../../contracts/utils/ICS20Lib.sol"; +import { ICS24Host } from "../../contracts/utils/ICS24Host.sol"; import { Strings } from "@openzeppelin/utils/Strings.sol"; import { Vm } from "forge-std/Vm.sol"; diff --git a/test/fixtures/acknowledgeMultiPacket_1-groth16.json b/test/solidity-ibc/fixtures/acknowledgeMultiPacket_1-groth16.json similarity index 100% rename from test/fixtures/acknowledgeMultiPacket_1-groth16.json rename to test/solidity-ibc/fixtures/acknowledgeMultiPacket_1-groth16.json diff --git a/test/fixtures/acknowledgeMultiPacket_1-plonk.json b/test/solidity-ibc/fixtures/acknowledgeMultiPacket_1-plonk.json similarity index 100% rename from test/fixtures/acknowledgeMultiPacket_1-plonk.json rename to test/solidity-ibc/fixtures/acknowledgeMultiPacket_1-plonk.json diff --git a/test/fixtures/acknowledgeMultiPacket_25-groth16.json b/test/solidity-ibc/fixtures/acknowledgeMultiPacket_25-groth16.json similarity index 100% rename from test/fixtures/acknowledgeMultiPacket_25-groth16.json rename to test/solidity-ibc/fixtures/acknowledgeMultiPacket_25-groth16.json diff --git a/test/fixtures/acknowledgeMultiPacket_50-groth16.json b/test/solidity-ibc/fixtures/acknowledgeMultiPacket_50-groth16.json similarity index 100% rename from test/fixtures/acknowledgeMultiPacket_50-groth16.json rename to test/solidity-ibc/fixtures/acknowledgeMultiPacket_50-groth16.json diff --git a/test/fixtures/acknowledgeMultiPacket_50-plonk.json b/test/solidity-ibc/fixtures/acknowledgeMultiPacket_50-plonk.json similarity index 100% rename from test/fixtures/acknowledgeMultiPacket_50-plonk.json rename to test/solidity-ibc/fixtures/acknowledgeMultiPacket_50-plonk.json diff --git a/test/fixtures/receiveMultiPacket_1-groth16.json b/test/solidity-ibc/fixtures/receiveMultiPacket_1-groth16.json similarity index 100% rename from test/fixtures/receiveMultiPacket_1-groth16.json rename to test/solidity-ibc/fixtures/receiveMultiPacket_1-groth16.json diff --git a/test/fixtures/receiveMultiPacket_1-plonk.json b/test/solidity-ibc/fixtures/receiveMultiPacket_1-plonk.json similarity index 100% rename from test/fixtures/receiveMultiPacket_1-plonk.json rename to test/solidity-ibc/fixtures/receiveMultiPacket_1-plonk.json diff --git a/test/fixtures/receiveMultiPacket_25-groth16.json b/test/solidity-ibc/fixtures/receiveMultiPacket_25-groth16.json similarity index 100% rename from test/fixtures/receiveMultiPacket_25-groth16.json rename to test/solidity-ibc/fixtures/receiveMultiPacket_25-groth16.json diff --git a/test/fixtures/receiveMultiPacket_50-groth16.json b/test/solidity-ibc/fixtures/receiveMultiPacket_50-groth16.json similarity index 100% rename from test/fixtures/receiveMultiPacket_50-groth16.json rename to test/solidity-ibc/fixtures/receiveMultiPacket_50-groth16.json diff --git a/test/fixtures/receiveMultiPacket_50-plonk.json b/test/solidity-ibc/fixtures/receiveMultiPacket_50-plonk.json similarity index 100% rename from test/fixtures/receiveMultiPacket_50-plonk.json rename to test/solidity-ibc/fixtures/receiveMultiPacket_50-plonk.json diff --git a/test/fixtures/receiveNativePacket-groth16.json b/test/solidity-ibc/fixtures/receiveNativePacket-groth16.json similarity index 100% rename from test/fixtures/receiveNativePacket-groth16.json rename to test/solidity-ibc/fixtures/receiveNativePacket-groth16.json diff --git a/test/fixtures/receiveNativePacket-plonk.json b/test/solidity-ibc/fixtures/receiveNativePacket-plonk.json similarity index 100% rename from test/fixtures/receiveNativePacket-plonk.json rename to test/solidity-ibc/fixtures/receiveNativePacket-plonk.json diff --git a/test/fixtures/timeoutPacket-groth16.json b/test/solidity-ibc/fixtures/timeoutPacket-groth16.json similarity index 100% rename from test/fixtures/timeoutPacket-groth16.json rename to test/solidity-ibc/fixtures/timeoutPacket-groth16.json diff --git a/test/fixtures/timeoutPacket-plonk.json b/test/solidity-ibc/fixtures/timeoutPacket-plonk.json similarity index 100% rename from test/fixtures/timeoutPacket-plonk.json rename to test/solidity-ibc/fixtures/timeoutPacket-plonk.json diff --git a/test/mocks/DummyLightClient.sol b/test/solidity-ibc/mocks/DummyLightClient.sol similarity index 94% rename from test/mocks/DummyLightClient.sol rename to test/solidity-ibc/mocks/DummyLightClient.sol index 0309a1e7..963a77ec 100644 --- a/test/mocks/DummyLightClient.sol +++ b/test/solidity-ibc/mocks/DummyLightClient.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.28; // solhint-disable no-empty-blocks -import { ILightClient } from "../../contracts/interfaces/ILightClient.sol"; +import { ILightClient } from "../../../contracts/interfaces/ILightClient.sol"; contract DummyLightClient is ILightClient { UpdateResult public updateResult; diff --git a/test/mocks/ErroneousIBCStore.sol b/test/solidity-ibc/mocks/ErroneousIBCStore.sol similarity index 88% rename from test/mocks/ErroneousIBCStore.sol rename to test/solidity-ibc/mocks/ErroneousIBCStore.sol index 4a30a8e4..cc43e504 100644 --- a/test/mocks/ErroneousIBCStore.sol +++ b/test/solidity-ibc/mocks/ErroneousIBCStore.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.28; // solhint-disable no-empty-blocks -import { IIBCStore } from "../../contracts/interfaces/IIBCStore.sol"; -import { IICS26RouterMsgs } from "../../contracts/msgs/IICS26RouterMsgs.sol"; +import { IIBCStore } from "../../../contracts/interfaces/IIBCStore.sol"; +import { IICS26RouterMsgs } from "../../../contracts/msgs/IICS26RouterMsgs.sol"; /// @title Erroneous IBC Store /// @dev This contract is used to override some functions of the IBC store contract using cheatcodes diff --git a/test/mocks/TestERC20.sol b/test/solidity-ibc/mocks/TestERC20.sol similarity index 100% rename from test/mocks/TestERC20.sol rename to test/solidity-ibc/mocks/TestERC20.sol diff --git a/test/sp1-ics07/LargeMembership.t.sol b/test/sp1-ics07/LargeMembership.t.sol new file mode 100644 index 000000000..32057648 --- /dev/null +++ b/test/sp1-ics07/LargeMembership.t.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +// solhint-disable-next-line no-global-import +import "forge-std/console.sol"; +import { MembershipTest } from "./MembershipTest.sol"; +import { ILightClient } from "../../contracts/interfaces/ILightClient.sol"; + +contract SP1ICS07LargeMembershipTest is MembershipTest { + SP1MembershipProof public proof; + + function setUpLargeMembershipTestWithFixture(string memory fileName) public { + setUpTestWithFixtures(fileName); + + proof = abi.decode(fixture.membershipProof.proof, (SP1MembershipProof)); + } + + function getOutput() public view returns (MembershipOutput memory) { + return abi.decode(proof.sp1Proof.publicValues, (MembershipOutput)); + } + + function test_ValidLargeCachedVerifyMembership_25_plonk() public { + ValidCachedMulticallMembershipTest("membership_25-plonk_fixture.json", 25, "25 key-value pairs with plonk"); + } + + function test_ValidLargeCachedVerifyMembership_100_groth16() public { + ValidCachedMulticallMembershipTest( + "membership_100-groth16_fixture.json", 100, "100 key-value pairs with groth16" + ); + } + + function ValidCachedMulticallMembershipTest(string memory fileName, uint32 n, string memory metadata) public { + require(n > 0, "n must be greater than 0"); + + setUpLargeMembershipTestWithFixture(fileName); + + bytes[] memory multicallData = new bytes[](n); + + multicallData[0] = abi.encodeCall( + ILightClient.membership, + MsgMembership({ + proof: abi.encode(fixture.membershipProof), + proofHeight: fixture.proofHeight, + path: getOutput().kvPairs[0].path, + value: getOutput().kvPairs[0].value + }) + ); + + for (uint32 i = 1; i < n; i++) { + multicallData[i] = abi.encodeCall( + ILightClient.membership, + MsgMembership({ + proof: bytes(""), // cached kv pairs + proofHeight: fixture.proofHeight, + path: getOutput().kvPairs[i].path, + value: getOutput().kvPairs[i].value + }) + ); + } + + ics07Tendermint.multicall(multicallData); + console.log("Proved", metadata, ", gas used: ", vm.lastCallGas().gasTotalUsed); + } +} diff --git a/test/sp1-ics07/Membership.t.sol b/test/sp1-ics07/Membership.t.sol new file mode 100644 index 000000000..6e95d5fd --- /dev/null +++ b/test/sp1-ics07/Membership.t.sol @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +// solhint-disable-next-line no-global-import +import "forge-std/console.sol"; +import { MembershipTest } from "./MembershipTest.sol"; + +contract SP1ICS07MembershipTest is MembershipTest { + SP1MembershipProof public proof; + + function setUpMembershipTestWithFixture(string memory fileName) public { + setUpTestWithFixtures(fileName); + + proof = abi.decode(fixture.membershipProof.proof, (SP1MembershipProof)); + } + + function fixtureTestCases() public pure returns (FixtureTestCase[] memory) { + FixtureTestCase[] memory testCases = new FixtureTestCase[](2); + testCases[0] = FixtureTestCase({ name: "groth16", fileName: "memberships_fixture-groth16.json" }); + testCases[1] = FixtureTestCase({ name: "plonk", fileName: "memberships_fixture-plonk.json" }); + + return testCases; + } + + function test_ValidateFixtures() public { + FixtureTestCase[] memory testCases = fixtureTestCases(); + + for (uint256 i = 0; i < testCases.length; i++) { + FixtureTestCase memory tc = testCases[i]; + setUpMembershipTestWithFixture(tc.fileName); + + MembershipOutput memory output = abi.decode(proof.sp1Proof.publicValues, (MembershipOutput)); + + assertEq(output.kvPairs.length, 2); + assertEq(output.kvPairs[0].path, verifyMembershipPath); + assert(output.kvPairs[0].value.length != 0); + assertEq(output.kvPairs[0].value, VERIFY_MEMBERSHIP_VALUE); + assertEq(output.kvPairs[1].path, verifyNonMembershipPath); + assertEq(output.kvPairs[1].value.length, 0); + } + } + + // Confirm that submitting a real proof passes the verifier. + function test_ValidVerifyMembership() public { + FixtureTestCase[] memory testCases = fixtureTestCases(); + + for (uint256 i = 0; i < testCases.length; i++) { + FixtureTestCase memory tc = testCases[i]; + setUpMembershipTestWithFixture(tc.fileName); + + MsgMembership memory membershipMsg = MsgMembership({ + proof: abi.encode(fixture.membershipProof), + proofHeight: fixture.proofHeight, + path: verifyMembershipPath, + value: VERIFY_MEMBERSHIP_VALUE + }); + + ics07Tendermint.membership(membershipMsg); + + console.log("VerifyMembership-", testCases[i].name, " gas used: ", vm.lastCallGas().gasTotalUsed); + } + } + + // Modify the proof to make it a non-membership proof. + function test_ValidVerifyNonMembership() public { + FixtureTestCase[] memory testCases = fixtureTestCases(); + + for (uint256 i = 0; i < testCases.length; i++) { + FixtureTestCase memory tc = testCases[i]; + setUpMembershipTestWithFixture(tc.fileName); + + MsgMembership memory nonMembershipMsg = MsgMembership({ + proof: abi.encode(fixture.membershipProof), + proofHeight: fixture.proofHeight, + path: verifyNonMembershipPath, + value: bytes("") + }); + + ics07Tendermint.membership(nonMembershipMsg); + + console.log("VerifyNonMembership-", testCases[i].name, " gas used: ", vm.lastCallGas().gasTotalUsed); + } + } + + function test_ValidCachedMembership() public { + // It doesn't matter which fixture we use, as proofs will be cached + setUpMembershipTestWithFixture("memberships_fixture-plonk.json"); + + MsgMembership memory membershipMsg = MsgMembership({ + proof: abi.encode(fixture.membershipProof), + proofHeight: fixture.proofHeight, + path: verifyMembershipPath, + value: VERIFY_MEMBERSHIP_VALUE + }); + + ics07Tendermint.membership(membershipMsg); + + // resubmit cached membership proof + MsgMembership memory cachedMembershipMsg = MsgMembership({ + proof: bytes(""), + proofHeight: fixture.proofHeight, + path: verifyMembershipPath, + value: VERIFY_MEMBERSHIP_VALUE + }); + ics07Tendermint.membership(cachedMembershipMsg); + + console.log("Cached VerifyMembership gas used: ", vm.lastCallGas().gasTotalUsed); + + // resubmit cached non-membership proof + MsgMembership memory cachedNonMembershipMsg = MsgMembership({ + proof: bytes(""), + proofHeight: fixture.proofHeight, + path: verifyNonMembershipPath, + value: bytes("") + }); + + ics07Tendermint.membership(cachedNonMembershipMsg); + + console.log("Cached VerifyNonMembership gas used: ", vm.lastCallGas().gasTotalUsed); + + // resubmit invalid cached membership proof + MsgMembership memory invalidCachedMembershipMsg = MsgMembership({ + proof: bytes(""), + proofHeight: fixture.proofHeight, + path: verifyMembershipPath, + value: bytes("invalid") + }); + vm.expectRevert(abi.encodeWithSelector(KeyValuePairNotInCache.selector, verifyMembershipPath, bytes("invalid"))); + ics07Tendermint.membership(invalidCachedMembershipMsg); + } + + // Confirm that submitting an invalid proof with the real verifier fails. + function test_Invalid_VerifyMembership() public { + FixtureTestCase[] memory testCases = fixtureTestCases(); + + for (uint256 i = 0; i < testCases.length; i++) { + FixtureTestCase memory tc = testCases[i]; + setUpMembershipTestWithFixture(tc.fileName); + + SP1MembershipProof memory proofMsg = proof; + proofMsg.sp1Proof.proof = bytes("invalid"); + + MembershipProof memory membershipProof = + MembershipProof({ proofType: MembershipProofType.SP1MembershipProof, proof: abi.encode(proofMsg) }); + + MsgMembership memory membershipMsg = MsgMembership({ + proof: abi.encode(membershipProof), + proofHeight: fixture.proofHeight, + path: verifyNonMembershipPath, + value: bytes("") + }); + + vm.expectRevert(); + ics07Tendermint.membership(membershipMsg); + } + } + + function test_Invalid_MockMembership() public { + // It doesn't matter which fixture we use, as we use mock verifier + setUpMembershipTestWithFixture("memberships_fixture-plonk.json"); + + MockInvalidMembershipTestCase[] memory testCases = new MockInvalidMembershipTestCase[](10); + testCases[0] = MockInvalidMembershipTestCase({ + name: "success: valid mock", + sp1Proof: SP1Proof({ proof: bytes(""), publicValues: proof.sp1Proof.publicValues, vKey: proof.sp1Proof.vKey }), + proofHeight: fixture.proofHeight.revisionHeight, + path: verifyNonMembershipPath, + value: bytes(""), + expPass: true + }); + testCases[1] = MockInvalidMembershipTestCase({ + name: "Invalid proof", + sp1Proof: SP1Proof({ + proof: bytes("invalid"), + publicValues: proof.sp1Proof.publicValues, + vKey: proof.sp1Proof.vKey + }), + proofHeight: fixture.proofHeight.revisionHeight, + path: verifyNonMembershipPath, + value: bytes(""), + expPass: false + }); + testCases[2] = MockInvalidMembershipTestCase({ + name: "Invalid proof height", + sp1Proof: SP1Proof({ proof: bytes(""), publicValues: proof.sp1Proof.publicValues, vKey: proof.sp1Proof.vKey }), + proofHeight: fixture.proofHeight.revisionHeight + 1, + path: verifyNonMembershipPath, + value: bytes(""), + expPass: false + }); + testCases[3] = MockInvalidMembershipTestCase({ + name: "Empty path", + sp1Proof: SP1Proof({ proof: bytes(""), publicValues: proof.sp1Proof.publicValues, vKey: proof.sp1Proof.vKey }), + proofHeight: fixture.proofHeight.revisionHeight, + path: new bytes[](0), + value: bytes(""), + expPass: false + }); + testCases[4] = MockInvalidMembershipTestCase({ + name: "Invalid prefix", + sp1Proof: SP1Proof({ proof: bytes(""), publicValues: proof.sp1Proof.publicValues, vKey: proof.sp1Proof.vKey }), + proofHeight: fixture.proofHeight.revisionHeight, + path: verifyNonMembershipPath, + value: bytes(""), + expPass: false + }); + testCases[4].path[0] = bytes("abc"); + testCases[5] = MockInvalidMembershipTestCase({ + name: "Invalid prefix, same length", + sp1Proof: SP1Proof({ proof: bytes(""), publicValues: proof.sp1Proof.publicValues, vKey: proof.sp1Proof.vKey }), + proofHeight: fixture.proofHeight.revisionHeight, + path: verifyNonMembershipPath, + value: bytes(""), + expPass: false + }); + testCases[5].path[0] = bytes("invalid"); + testCases[6] = MockInvalidMembershipTestCase({ + name: "Invalid suffix", + sp1Proof: SP1Proof({ proof: bytes(""), publicValues: proof.sp1Proof.publicValues, vKey: proof.sp1Proof.vKey }), + proofHeight: fixture.proofHeight.revisionHeight, + path: verifyNonMembershipPath, + value: bytes(""), + expPass: false + }); + testCases[6].path[1] = bytes("invalid"); + testCases[7] = MockInvalidMembershipTestCase({ + name: "Invalid value", + sp1Proof: SP1Proof({ proof: bytes(""), publicValues: proof.sp1Proof.publicValues, vKey: proof.sp1Proof.vKey }), + proofHeight: fixture.proofHeight.revisionHeight, + path: verifyNonMembershipPath, + value: bytes("invalid"), + expPass: false + }); + testCases[8] = MockInvalidMembershipTestCase({ + name: "Invalid vKey", + sp1Proof: SP1Proof({ + proof: bytes(""), + publicValues: proof.sp1Proof.publicValues, + vKey: genesisFixture.ucAndMembershipVkey + }), + proofHeight: fixture.proofHeight.revisionHeight, + path: verifyNonMembershipPath, + value: bytes(""), + expPass: false + }); + testCases[9] = MockInvalidMembershipTestCase({ + name: "Invalid public values", + sp1Proof: SP1Proof({ proof: bytes(""), publicValues: bytes("invalid"), vKey: proof.sp1Proof.vKey }), + proofHeight: fixture.proofHeight.revisionHeight, + path: verifyNonMembershipPath, + value: bytes(""), + expPass: false + }); + + for (uint256 i = 0; i < testCases.length; i++) { + MockInvalidMembershipTestCase memory tc = testCases[i]; + + SP1MembershipProof memory proofMsg = proof; + proofMsg.sp1Proof = tc.sp1Proof; + + MembershipProof memory membershipProof = + MembershipProof({ proofType: MembershipProofType.SP1MembershipProof, proof: abi.encode(proofMsg) }); + + Height memory proofHeight = fixture.proofHeight; + proofHeight.revisionHeight = tc.proofHeight; + + MsgMembership memory membershipMsg = MsgMembership({ + proof: abi.encode(membershipProof), + proofHeight: proofHeight, + path: tc.path, + value: tc.value + }); + + if (tc.expPass) { + mockIcs07Tendermint.membership(membershipMsg); + } else { + vm.expectRevert(); + mockIcs07Tendermint.membership(membershipMsg); + } + } + } + + struct MockInvalidMembershipTestCase { + string name; + SP1Proof sp1Proof; + uint32 proofHeight; + bytes[] path; + bytes value; + bool expPass; + } +} diff --git a/test/sp1-ics07/MembershipTest.sol b/test/sp1-ics07/MembershipTest.sol new file mode 100644 index 000000000..0cb1d393 --- /dev/null +++ b/test/sp1-ics07/MembershipTest.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +// solhint-disable-next-line no-global-import +import "forge-std/console.sol"; +import { stdJson } from "forge-std/StdJson.sol"; +import { SP1ICS07TendermintTest } from "./SP1ICS07TendermintTest.sol"; + +abstract contract MembershipTest is SP1ICS07TendermintTest { + bytes[] public verifyMembershipPath = [bytes("ibc"), bytes("clients/07-tendermint-0/clientState")]; + + bytes[] public verifyNonMembershipPath = [bytes("ibc"), bytes("clients/07-tendermint-001/clientState")]; + + bytes public constant VERIFY_MEMBERSHIP_VALUE = + hex"0a2b2f6962632e6c69676874636c69656e74732e74656e6465726d696e742e76312e436c69656e7453746174651287010a1174686574612d746573746e65742d3030311204080110031a040880840722040880c60a2a02082832003a0510b7e3c60842190a090801180120012a0100120c0a02000110211804200c300142190a090801180120012a0100120c0a02000110201801200130014a07757067726164654a107570677261646564494243537461746550015801"; + + struct SP1ICS07MembershipFixtureJson { + Height proofHeight; + MembershipProof membershipProof; + } + + using stdJson for string; + + SP1ICS07MembershipFixtureJson public fixture; + + function setUpTestWithFixtures(string memory fileName) public { + fixture = loadFixture(fileName); + + setUpTest(fileName); + } + + function loadFixture(string memory fileName) public view returns (SP1ICS07MembershipFixtureJson memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, FIXTURE_DIR, fileName); + string memory json = vm.readFile(path); + bytes memory proofHeightBz = json.readBytes(".proofHeight"); + bytes memory membershipProofBz = json.readBytes(".membershipProof"); + + SP1ICS07MembershipFixtureJson memory fix = SP1ICS07MembershipFixtureJson({ + proofHeight: abi.decode(proofHeightBz, (Height)), + membershipProof: abi.decode(membershipProofBz, (MembershipProof)) + }); + + return fix; + } +} diff --git a/test/sp1-ics07/Misbehaviour.t.sol b/test/sp1-ics07/Misbehaviour.t.sol new file mode 100644 index 000000000..da46a8c5 --- /dev/null +++ b/test/sp1-ics07/Misbehaviour.t.sol @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +// solhint-disable-next-line no-global-import +import "forge-std/console.sol"; +import { SP1ICS07TendermintTest } from "./SP1ICS07TendermintTest.sol"; +import { IMisbehaviourMsgs } from "../../contracts/light-clients/msgs/IMisbehaviourMsgs.sol"; +import { SP1Verifier } from "@sp1-contracts/v3.0.0/SP1VerifierPlonk.sol"; +import { stdJson } from "forge-std/StdJson.sol"; + +struct SP1ICS07MisbehaviourFixtureJson { + bytes trustedClientState; + bytes trustedConsensusState; + bytes submitMsg; +} + +contract SP1ICS07MisbehaviourTest is SP1ICS07TendermintTest { + using stdJson for string; + + SP1ICS07MisbehaviourFixtureJson public fixture; + MsgSubmitMisbehaviour public submitMsg; + MisbehaviourOutput public output; + + function setUpMisbehaviour(string memory fileName) public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, FIXTURE_DIR, fileName); + string memory json = vm.readFile(path); + bytes memory trustedClientStateBz = json.readBytes(".trustedClientState"); + bytes memory trustedConsensusStateBz = json.readBytes(".trustedConsensusState"); + bytes memory submitMsgBz = json.readBytes(".submitMsg"); + + fixture = SP1ICS07MisbehaviourFixtureJson({ + trustedClientState: trustedClientStateBz, + trustedConsensusState: trustedConsensusStateBz, + submitMsg: submitMsgBz + }); + + setUpTest(fileName); + + submitMsg = abi.decode(fixture.submitMsg, (IMisbehaviourMsgs.MsgSubmitMisbehaviour)); + output = abi.decode(submitMsg.sp1Proof.publicValues, (IMisbehaviourMsgs.MisbehaviourOutput)); + } + + function test_ValidDoubleSignMisbehaviour() public { + setUpMisbehaviour("misbehaviour_double_sign-plonk_fixture.json"); + + // set a correct timestamp + vm.warp(output.time); + ics07Tendermint.misbehaviour(fixture.submitMsg); + + // to console + console.log("Misbehaviour gas used: ", vm.lastCallGas().gasTotalUsed); + + // verify that the client is frozen + ClientState memory clientState = ics07Tendermint.getClientState(); + assertTrue(clientState.isFrozen); + } + + function test_ValidBreakingTimeMonotonicityMisbehaviour() public { + setUpMisbehaviour("misbehaviour_breaking_time_monotonicity-groth16_fixture.json"); + + // set a correct timestamp + vm.warp(output.time); + ics07Tendermint.misbehaviour(fixture.submitMsg); + + // to console + console.log("Misbehaviour gas used: ", vm.lastCallGas().gasTotalUsed); + + // verify that the client is frozen + ClientState memory clientState = ics07Tendermint.getClientState(); + assertTrue(clientState.isFrozen); + } + + function test_FrozenClientState() public { + setUpMisbehaviour("misbehaviour_double_sign-plonk_fixture.json"); + + // set a correct timestamp + vm.warp(output.time); + ics07Tendermint.misbehaviour(fixture.submitMsg); + + // verify that the client is frozen + ClientState memory clientState = ics07Tendermint.getClientState(); + assertTrue(clientState.isFrozen); + + // try to submit a updateClient msg + vm.expectRevert(abi.encodeWithSelector(FrozenClientState.selector)); + ics07Tendermint.updateClient(bytes("")); + + // try to submit a membership msg + MsgMembership memory membership; + vm.expectRevert(abi.encodeWithSelector(FrozenClientState.selector)); + ics07Tendermint.membership(membership); + + // try to submit a misbehaviour msg + vm.expectRevert(abi.encodeWithSelector(FrozenClientState.selector)); + ics07Tendermint.misbehaviour(fixture.submitMsg); + + // try to submit upgrade client + vm.expectRevert(abi.encodeWithSelector(FrozenClientState.selector)); + ics07Tendermint.upgradeClient(bytes("")); + } + + function test_InvalidMisbehaviour() public { + setUpMisbehaviour("misbehaviour_double_sign-plonk_fixture.json"); + + // proof is in the future + vm.warp(output.time - 300); + vm.expectRevert(abi.encodeWithSelector(ProofIsInTheFuture.selector, block.timestamp, output.time)); + ics07Tendermint.misbehaviour(fixture.submitMsg); + + // proof is too old + vm.warp(output.time + ics07Tendermint.ALLOWED_SP1_CLOCK_DRIFT() + 300); + vm.expectRevert(abi.encodeWithSelector(ProofIsTooOld.selector, block.timestamp, output.time)); + ics07Tendermint.misbehaviour(fixture.submitMsg); + + // set a correct timestamp + vm.warp(output.time + 300); + + // wrong vkey + MsgSubmitMisbehaviour memory badSubmitMsg = cloneSubmitMsg(); + badSubmitMsg.sp1Proof.vKey = bytes32(0); + bytes memory submitMsgBz = abi.encode(badSubmitMsg); + vm.expectRevert( + abi.encodeWithSelector( + VerificationKeyMismatch.selector, + ics07Tendermint.MISBEHAVIOUR_PROGRAM_VKEY(), + badSubmitMsg.sp1Proof.vKey + ) + ); + ics07Tendermint.misbehaviour(submitMsgBz); + + // chain id mismatch + badSubmitMsg = cloneSubmitMsg(); + MisbehaviourOutput memory badOutput = cloneOutput(); + badOutput.clientState.chainId = "bad-chain-id"; + badSubmitMsg.sp1Proof.publicValues = abi.encode(badOutput); + submitMsgBz = abi.encode(badSubmitMsg); + vm.expectRevert( + abi.encodeWithSelector(ChainIdMismatch.selector, output.clientState.chainId, badOutput.clientState.chainId) + ); + ics07Tendermint.misbehaviour(submitMsgBz); + + // trust threshold mismatch + badSubmitMsg = cloneSubmitMsg(); + badOutput = cloneOutput(); + badOutput.clientState.trustLevel = TrustThreshold({ numerator: 1, denominator: 2 }); + badSubmitMsg.sp1Proof.publicValues = abi.encode(badOutput); + submitMsgBz = abi.encode(badSubmitMsg); + vm.expectRevert( + abi.encodeWithSelector( + TrustThresholdMismatch.selector, + output.clientState.trustLevel.numerator, + output.clientState.trustLevel.denominator, + badOutput.clientState.trustLevel.numerator, + badOutput.clientState.trustLevel.denominator + ) + ); + ics07Tendermint.misbehaviour(submitMsgBz); + + // trusting period mismatch + badSubmitMsg = cloneSubmitMsg(); + badOutput = cloneOutput(); + badOutput.clientState.trustingPeriod = 1; + badSubmitMsg.sp1Proof.publicValues = abi.encode(badOutput); + submitMsgBz = abi.encode(badSubmitMsg); + vm.expectRevert( + abi.encodeWithSelector( + TrustingPeriodMismatch.selector, output.clientState.trustingPeriod, badOutput.clientState.trustingPeriod + ) + ); + ics07Tendermint.misbehaviour(submitMsgBz); + + // invalid proof + badSubmitMsg = cloneSubmitMsg(); + badOutput = cloneOutput(); + badOutput.time = badOutput.time + 1; + badSubmitMsg.sp1Proof.publicValues = abi.encode(badOutput); + submitMsgBz = abi.encode(badSubmitMsg); + vm.expectRevert(abi.encodeWithSelector(SP1Verifier.InvalidProof.selector)); + ics07Tendermint.misbehaviour(submitMsgBz); + + // client state is frozen + ics07Tendermint.misbehaviour(fixture.submitMsg); // freeze the client + vm.expectRevert(abi.encodeWithSelector(FrozenClientState.selector)); + ics07Tendermint.misbehaviour(fixture.submitMsg); + } + + function cloneSubmitMsg() private view returns (MsgSubmitMisbehaviour memory) { + MsgSubmitMisbehaviour memory clone = MsgSubmitMisbehaviour({ + sp1Proof: SP1Proof({ + vKey: submitMsg.sp1Proof.vKey, + publicValues: submitMsg.sp1Proof.publicValues, + proof: submitMsg.sp1Proof.proof + }) + }); + return clone; + } + + function cloneOutput() private view returns (MisbehaviourOutput memory) { + MisbehaviourOutput memory clone = MisbehaviourOutput({ + clientState: output.clientState, + time: output.time, + trustedHeight1: output.trustedHeight1, + trustedHeight2: output.trustedHeight2, + trustedConsensusState1: output.trustedConsensusState1, + trustedConsensusState2: output.trustedConsensusState2 + }); + return clone; + } +} diff --git a/test/sp1-ics07/SP1ICS07TendermintTest.sol b/test/sp1-ics07/SP1ICS07TendermintTest.sol new file mode 100644 index 000000000..cab9a951 --- /dev/null +++ b/test/sp1-ics07/SP1ICS07TendermintTest.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +// solhint-disable-next-line no-global-import +import "forge-std/console.sol"; +import { Test, stdStorage, StdStorage } from "forge-std/Test.sol"; +import { stdJson } from "forge-std/StdJson.sol"; +import { IICS07TendermintMsgs } from "../../contracts/light-clients/msgs/IICS07TendermintMsgs.sol"; +import { IUpdateClientMsgs } from "../../contracts/light-clients/msgs/IUpdateClientMsgs.sol"; +import { IMembershipMsgs } from "../../contracts/light-clients/msgs/IMembershipMsgs.sol"; +import { IUpdateClientAndMembershipMsgs } from "../../contracts/light-clients/msgs/IUcAndMembershipMsgs.sol"; +import { IMisbehaviourMsgs } from "../../contracts/light-clients/msgs/IMisbehaviourMsgs.sol"; +import { SP1ICS07Tendermint } from "../../contracts/light-clients/SP1ICS07Tendermint.sol"; +import { ISP1ICS07TendermintErrors } from "../../contracts/light-clients/errors/ISP1ICS07TendermintErrors.sol"; +import { ISP1Verifier } from "@sp1-contracts/ISP1Verifier.sol"; +import { SP1MockVerifier } from "@sp1-contracts/SP1MockVerifier.sol"; +import { ILightClientMsgs } from "../../contracts/msgs/ILightClientMsgs.sol"; + +struct SP1ICS07GenesisFixtureJson { + bytes trustedClientState; + bytes trustedConsensusState; + bytes32 updateClientVkey; + bytes32 membershipVkey; + bytes32 ucAndMembershipVkey; + bytes32 misbehaviourVkey; +} + +abstract contract SP1ICS07TendermintTest is + Test, + IICS07TendermintMsgs, + IUpdateClientMsgs, + IMembershipMsgs, + IUpdateClientAndMembershipMsgs, + IMisbehaviourMsgs, + ISP1ICS07TendermintErrors, + ILightClientMsgs +{ + using stdJson for string; + using stdStorage for StdStorage; + + SP1ICS07Tendermint public ics07Tendermint; + SP1ICS07Tendermint public mockIcs07Tendermint; + + SP1ICS07GenesisFixtureJson internal genesisFixture; + + string internal constant FIXTURE_DIR = "/test/sp1-ics07/fixtures/"; + + function setUpTest(string memory fileName) public { + genesisFixture = loadGenesisFixture(fileName); + + ConsensusState memory trustedConsensusState = abi.decode(genesisFixture.trustedConsensusState, (ConsensusState)); + + bytes32 trustedConsensusHash = keccak256(abi.encode(trustedConsensusState)); + + ics07Tendermint = new SP1ICS07Tendermint( + genesisFixture.updateClientVkey, + genesisFixture.membershipVkey, + genesisFixture.ucAndMembershipVkey, + genesisFixture.misbehaviourVkey, + genesisFixture.trustedClientState, + trustedConsensusHash + ); + + mockIcs07Tendermint = new SP1ICS07Tendermint( + genesisFixture.updateClientVkey, + genesisFixture.membershipVkey, + genesisFixture.ucAndMembershipVkey, + genesisFixture.misbehaviourVkey, + genesisFixture.trustedClientState, + trustedConsensusHash + ); + SP1MockVerifier mockVerifier = new SP1MockVerifier(); + vm.mockFunction( + address(mockIcs07Tendermint.VERIFIER()), + address(mockVerifier), + abi.encodeWithSelector(ISP1Verifier.verifyProof.selector) + ); + + ClientState memory clientState = mockIcs07Tendermint.getClientState(); + assert(keccak256(abi.encode(clientState)) == keccak256(genesisFixture.trustedClientState)); + + bytes32 consensusHash = mockIcs07Tendermint.getConsensusStateHash(clientState.latestHeight.revisionHeight); + assert(consensusHash == trustedConsensusHash); + } + + function loadGenesisFixture(string memory fileName) public view returns (SP1ICS07GenesisFixtureJson memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, FIXTURE_DIR, fileName); + string memory json = vm.readFile(path); + bytes memory trustedClientState = json.readBytes(".trustedClientState"); + bytes memory trustedConsensusState = json.readBytes(".trustedConsensusState"); + bytes32 updateClientVkey = json.readBytes32(".updateClientVkey"); + bytes32 membershipVkey = json.readBytes32(".membershipVkey"); + bytes32 ucAndMembershipVkey = json.readBytes32(".ucAndMembershipVkey"); + bytes32 misbehaviourVkey = json.readBytes32(".misbehaviourVkey"); + + SP1ICS07GenesisFixtureJson memory fix = SP1ICS07GenesisFixtureJson({ + trustedClientState: trustedClientState, + trustedConsensusState: trustedConsensusState, + updateClientVkey: updateClientVkey, + membershipVkey: membershipVkey, + ucAndMembershipVkey: ucAndMembershipVkey, + misbehaviourVkey: misbehaviourVkey + }); + + return fix; + } + + struct FixtureTestCase { + string name; + string fileName; + } +} diff --git a/test/sp1-ics07/UcAndMembership.t.sol b/test/sp1-ics07/UcAndMembership.t.sol new file mode 100644 index 000000000..3b2407cb --- /dev/null +++ b/test/sp1-ics07/UcAndMembership.t.sol @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +// solhint-disable-next-line no-global-import +import "forge-std/console.sol"; +import { stdJson } from "forge-std/StdJson.sol"; +import { MembershipTest } from "./MembershipTest.sol"; + +contract SP1ICS07UpdateClientAndMembershipTest is MembershipTest { + using stdJson for string; + + SP1MembershipAndUpdateClientProof public proof; + + function setUpUcAndMemTestWithFixtures(string memory fileName) public { + setUpTestWithFixtures(fileName); + + proof = abi.decode(fixture.membershipProof.proof, (SP1MembershipAndUpdateClientProof)); + + UcAndMembershipOutput memory output = abi.decode(proof.sp1Proof.publicValues, (UcAndMembershipOutput)); + + ClientState memory clientState = mockIcs07Tendermint.getClientState(); + assert(clientState.latestHeight.revisionHeight < output.updateClientOutput.newHeight.revisionHeight); + } + + function fixtureTestCases() public pure returns (FixtureTestCase[] memory) { + FixtureTestCase[] memory testCases = new FixtureTestCase[](2); + testCases[0] = FixtureTestCase({ name: "groth16", fileName: "uc_and_memberships_fixture-groth16.json" }); + testCases[1] = FixtureTestCase({ name: "plonk", fileName: "uc_and_memberships_fixture-plonk.json" }); + + return testCases; + } + + // Confirm that submitting a real proof passes the verifier. + function test_Valid_UpdateClientAndVerifyMembership() public { + FixtureTestCase[] memory testCases = fixtureTestCases(); + + for (uint256 i = 0; i < testCases.length; i++) { + setUpUcAndMemTestWithFixtures(testCases[i].fileName); + + UcAndMembershipOutput memory output = abi.decode(proof.sp1Proof.publicValues, (UcAndMembershipOutput)); + // set a correct timestamp + vm.warp(output.updateClientOutput.time + 300); + + MsgMembership memory membershipMsg = MsgMembership({ + proof: abi.encode(fixture.membershipProof), + proofHeight: fixture.proofHeight, + path: verifyMembershipPath, + value: VERIFY_MEMBERSHIP_VALUE + }); + + // run verify + ics07Tendermint.membership(membershipMsg); + + console.log( + "UpdateClientAndVerifyMembership-", testCases[i].name, "gas used: ", vm.lastCallGas().gasTotalUsed + ); + + ClientState memory clientState = ics07Tendermint.getClientState(); + assert(clientState.latestHeight.revisionHeight == output.updateClientOutput.newHeight.revisionHeight); + assert(clientState.isFrozen == false); + + bytes32 consensusHash = + ics07Tendermint.getConsensusStateHash(output.updateClientOutput.newHeight.revisionHeight); + assert(consensusHash == keccak256(abi.encode(output.updateClientOutput.newConsensusState))); + } + } + + // Confirm that submitting a real proof passes the verifier. + function test_Valid_UpdateClientAndVerifyNonMembership() public { + FixtureTestCase[] memory testCases = fixtureTestCases(); + + for (uint256 i = 0; i < testCases.length; i++) { + setUpUcAndMemTestWithFixtures(testCases[i].fileName); + + UcAndMembershipOutput memory output = abi.decode(proof.sp1Proof.publicValues, (UcAndMembershipOutput)); + // set a correct timestamp + vm.warp(output.updateClientOutput.time + 300); + + MsgMembership memory nonMembershipMsg = MsgMembership({ + proof: abi.encode(fixture.membershipProof), + proofHeight: fixture.proofHeight, + path: verifyNonMembershipPath, + value: bytes("") + }); + + // run verify + ics07Tendermint.membership(nonMembershipMsg); + + console.log( + "UpdateClientAndVerifyNonMembership-", testCases[i].name, "gas used: ", vm.lastCallGas().gasTotalUsed + ); + + ClientState memory clientState = ics07Tendermint.getClientState(); + assert(clientState.latestHeight.revisionHeight == output.updateClientOutput.newHeight.revisionHeight); + assert(clientState.isFrozen == false); + + bytes32 consensusHash = + ics07Tendermint.getConsensusStateHash(output.updateClientOutput.newHeight.revisionHeight); + assert(consensusHash == keccak256(abi.encode(output.updateClientOutput.newConsensusState))); + } + } + + // Confirm that submitting a real proof passes the verifier. + function test_Valid_CachedUpdateClientAndMembership() public { + // It doesn't matter which fixture we use since this is a cached proof + setUpUcAndMemTestWithFixtures("uc_and_memberships_fixture-groth16.json"); + + UcAndMembershipOutput memory output = abi.decode(proof.sp1Proof.publicValues, (UcAndMembershipOutput)); + // set a correct timestamp + vm.warp(output.updateClientOutput.time + 300); + + MsgMembership memory membershipMsg = MsgMembership({ + proof: abi.encode(fixture.membershipProof), + proofHeight: fixture.proofHeight, + path: verifyMembershipPath, + value: VERIFY_MEMBERSHIP_VALUE + }); + + // run verify + ics07Tendermint.membership(membershipMsg); + + ClientState memory clientState = ics07Tendermint.getClientState(); + assert(clientState.latestHeight.revisionHeight == output.updateClientOutput.newHeight.revisionHeight); + assert(clientState.isFrozen == false); + + bytes32 consensusHash = + ics07Tendermint.getConsensusStateHash(output.updateClientOutput.newHeight.revisionHeight); + assert(consensusHash == keccak256(abi.encode(output.updateClientOutput.newConsensusState))); + + // submit cached membership proof + MsgMembership memory cachedMembershipMsg = MsgMembership({ + proof: bytes(""), + proofHeight: fixture.proofHeight, + path: verifyMembershipPath, + value: VERIFY_MEMBERSHIP_VALUE + }); + ics07Tendermint.membership(cachedMembershipMsg); + + console.log("Cached UpdateClientAndVerifyMembership gas used: ", vm.lastCallGas().gasTotalUsed); + + // submit cached non-membership proof + MsgMembership memory nonMembershipMsg = MsgMembership({ + proof: bytes(""), + proofHeight: fixture.proofHeight, + path: verifyNonMembershipPath, + value: bytes("") + }); + + // run verify + ics07Tendermint.membership(nonMembershipMsg); + + console.log("Cached UpdateClientAndNonVerifyMembership gas used: ", vm.lastCallGas().gasTotalUsed); + } + + function test_Invalid_UpdateClientAndMembership() public { + // It doesn't matter which fixture we use since this is an invalid proof + setUpUcAndMemTestWithFixtures("uc_and_memberships_fixture-groth16.json"); + + UcAndMembershipOutput memory output = abi.decode(proof.sp1Proof.publicValues, (UcAndMembershipOutput)); + // set a correct timestamp + vm.warp(output.updateClientOutput.time + 300); + + SP1MembershipAndUpdateClientProof memory ucAndMemProof = proof; + ucAndMemProof.sp1Proof.proof = bytes("invalid"); + + MembershipProof memory membershipProof = MembershipProof({ + proofType: MembershipProofType.SP1MembershipAndUpdateClientProof, + proof: abi.encode(ucAndMemProof) + }); + + MsgMembership memory membershipMsg = MsgMembership({ + proof: abi.encode(membershipProof), + proofHeight: fixture.proofHeight, + path: verifyNonMembershipPath, + value: bytes("") + }); + + vm.expectRevert(); + ics07Tendermint.membership(membershipMsg); + } + + function test_MockMisbehavior_UpdateClientAndMembership() public { + // It doesn't matter which fixture we use since this is a mock contract + setUpUcAndMemTestWithFixtures("uc_and_memberships_fixture-groth16.json"); + + UcAndMembershipOutput memory output = abi.decode(proof.sp1Proof.publicValues, (UcAndMembershipOutput)); + // set a correct timestamp + vm.warp(output.updateClientOutput.time + 300); + + SP1MembershipAndUpdateClientProof memory ucAndMemProof = proof; + ucAndMemProof.sp1Proof.proof = bytes(""); + + MembershipProof memory membershipProof = MembershipProof({ + proofType: MembershipProofType.SP1MembershipAndUpdateClientProof, + proof: abi.encode(ucAndMemProof) + }); + + MsgMembership memory membershipMsg = MsgMembership({ + proof: abi.encode(membershipProof), + proofHeight: fixture.proofHeight, + path: verifyNonMembershipPath, + value: bytes("") + }); + + mockIcs07Tendermint.membership(membershipMsg); + + // change output so that it is a misbehaviour + output.updateClientOutput.newConsensusState.timestamp = output.updateClientOutput.time + 1; + // re-encode output + ucAndMemProof.sp1Proof.publicValues = abi.encode(output); + + membershipProof.proof = abi.encode(ucAndMemProof); + membershipMsg.proof = abi.encode(membershipProof); + + // run verify again + vm.expectRevert(abi.encodeWithSelector(CannotHandleMisbehavior.selector)); + mockIcs07Tendermint.membership(membershipMsg); + } +} diff --git a/test/sp1-ics07/UpdateClient.t.sol b/test/sp1-ics07/UpdateClient.t.sol new file mode 100644 index 000000000..f63527fd --- /dev/null +++ b/test/sp1-ics07/UpdateClient.t.sol @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +// solhint-disable-next-line no-global-import +import "forge-std/console.sol"; +import { stdJson } from "forge-std/StdJson.sol"; +import { SP1ICS07TendermintTest } from "./SP1ICS07TendermintTest.sol"; + +struct SP1ICS07UpdateClientFixtureJson { + bytes trustedClientState; + bytes trustedConsensusState; + bytes updateMsg; +} + +contract SP1ICS07UpdateClientTest is SP1ICS07TendermintTest { + using stdJson for string; + + SP1ICS07UpdateClientFixtureJson public fixture; + + UpdateClientOutput public output; + + function setUpTestWithFixture(string memory fileName) public { + fixture = loadFixture(fileName); + + setUpTest(fileName); + + MsgUpdateClient memory updateMsg = abi.decode(fixture.updateMsg, (MsgUpdateClient)); + output = abi.decode(updateMsg.sp1Proof.publicValues, (UpdateClientOutput)); + + ClientState memory clientState = mockIcs07Tendermint.getClientState(); + assert(clientState.latestHeight.revisionHeight < output.newHeight.revisionHeight); + + vm.expectRevert(); + mockIcs07Tendermint.getConsensusStateHash(output.newHeight.revisionHeight); + } + + function fixtureTestCases() public pure returns (FixtureTestCase[] memory) { + FixtureTestCase[] memory testCases = new FixtureTestCase[](2); + testCases[0] = FixtureTestCase({ name: "groth16", fileName: "update_client_fixture-groth16.json" }); + testCases[1] = FixtureTestCase({ name: "plonk", fileName: "update_client_fixture-plonk.json" }); + + return testCases; + } + + // Confirm that submitting a real proof passes the verifier. + function test_ValidUpdateClient() public { + FixtureTestCase[] memory testCases = fixtureTestCases(); + + for (uint256 i = 0; i < testCases.length; i++) { + setUpTestWithFixture(testCases[i].fileName); + + // set a correct timestamp + vm.warp(output.time + 300); + + // run verify + UpdateResult res = ics07Tendermint.updateClient(fixture.updateMsg); + + // to console + console.log("UpdateClient-", testCases[i].name, "gas used: ", vm.lastCallGas().gasTotalUsed); + assert(res == UpdateResult.Update); + + ClientState memory clientState = ics07Tendermint.getClientState(); + assert(keccak256(bytes(clientState.chainId)) == keccak256(bytes("mocha-4"))); + assert(clientState.latestHeight.revisionHeight == output.newHeight.revisionHeight); + assert(clientState.isFrozen == false); + + bytes32 consensusHash = ics07Tendermint.getConsensusStateHash(output.newHeight.revisionHeight); + assertEq(consensusHash, keccak256(abi.encode(output.newConsensusState))); + } + } + + // Confirm that submitting a real proof passes the verifier. + function test_ValidNoOpUpdateClient() public { + // Doesn't matter which fixture we use since this is a no-op + setUpTestWithFixture("update_client_fixture-plonk.json"); + // set a correct timestamp + vm.warp(output.time + 300); + + // run verify + UpdateResult res = ics07Tendermint.updateClient(fixture.updateMsg); + assert(res == UpdateResult.Update); + + // run verify again + res = ics07Tendermint.updateClient(fixture.updateMsg); + + // to console + console.log("UpdateClient_NoOp gas used: ", vm.lastCallGas().gasTotalUsed); + assert(res == UpdateResult.NoOp); + } + + function test_Invalid_UpdateClient() public { + // Doesn't matter which fixture we use since this is a fail + setUpTestWithFixture("update_client_fixture-plonk.json"); + + vm.expectRevert(); + ics07Tendermint.updateClient(bytes("invalid")); + } + + function test_MockMisbehavior_UpdateClient() public { + // Doesn't matter which fixture we use since this is a mock contract + setUpTestWithFixture("update_client_fixture-plonk.json"); + // set a correct timestamp + vm.warp(output.time + 300); + + // update mock client + MsgUpdateClient memory updateMsg = abi.decode(fixture.updateMsg, (MsgUpdateClient)); + updateMsg.sp1Proof.proof = bytes(""); + + UpdateResult res = mockIcs07Tendermint.updateClient(abi.encode(updateMsg)); + assert(res == UpdateResult.Update); + + // change output so that it is a misbehaviour + output.newConsensusState.timestamp = output.time + 1; + // re-encode output + updateMsg.sp1Proof.publicValues = abi.encode(output); + + // run verify again + res = mockIcs07Tendermint.updateClient(abi.encode(updateMsg)); + assert(res == UpdateResult.Misbehaviour); + assert(mockIcs07Tendermint.getClientState().isFrozen == true); + } + + function test_MockUpgradeClient() public { + // Doesn't matter which fixture we use since this is not implemented + setUpTestWithFixture("update_client_fixture-plonk.json"); + // set a correct timestamp + vm.warp(output.time + 300); + + // upgrade client + vm.expectRevert(abi.encodeWithSelector(FeatureNotSupported.selector)); + mockIcs07Tendermint.upgradeClient(bytes("")); + } + + function loadFixture(string memory fileName) public view returns (SP1ICS07UpdateClientFixtureJson memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, FIXTURE_DIR, fileName); + string memory json = vm.readFile(path); + bytes memory trustedClientState = json.readBytes(".trustedClientState"); + bytes memory trustedConsensusState = json.readBytes(".trustedConsensusState"); + bytes memory updateMsg = json.readBytes(".updateMsg"); + + SP1ICS07UpdateClientFixtureJson memory fix = SP1ICS07UpdateClientFixtureJson({ + trustedClientState: trustedClientState, + trustedConsensusState: trustedConsensusState, + updateMsg: updateMsg + }); + + return fix; + } +} diff --git a/test/sp1-ics07/fixtures/membership_100-groth16_fixture.json b/test/sp1-ics07/fixtures/membership_100-groth16_fixture.json new file mode 100644 index 000000000..16b95096 --- /dev/null +++ b/test/sp1-ics07/fixtures/membership_100-groth16_fixture.json @@ -0,0 +1,10 @@ +{ + "trustedClientState": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000012754500000000000000000000000000000000000000000000000000000000001baf8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000673696d642d310000000000000000000000000000000000000000000000000000", + "trustedConsensusState": "000000000000000000000000000000000000000000000000000000006749060d181c2370c37b61454caf176654975b2f0b68c3f139c99aa802d6af79bd40e045843906be33abdf97c694aec89ad2d391e7d1810c1247151954405ef337a1315b", + "updateClientVkey": "0x00ceb6e478b4abf450ee24d278ede0baf5568fd906b2c9f128cfa7eb83c2e225", + "membershipVkey": "0x007c90a390038b52fa1dd0d1b8e91bc7905dc81bb46274664c1bd41ce871b3f7", + "ucAndMembershipVkey": "0x006948fde1a874707ef63d93392cf0575860b605e972f5df2bf4d49dbb711d1c", + "misbehaviourVkey": "0x001df6ba765a3648a4b529447837712d6b6d3f4c72e9c9394c987b0b15b7a154", + "proofHeight": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000012", + "membershipProof": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000098e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000006749060d181c2370c37b61454caf176654975b2f0b68c3f139c99aa802d6af79bd40e045843906be33abdf97c694aec89ad2d391e7d1810c1247151954405ef337a1315b007c90a390038b52fa1dd0d1b8e91bc7905dc81bb46274664c1bd41ce871b3f70000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000970000000000000000000000000000000000000000000000000000000000000096800000000000000000000000000000000000000000000000000000000000000020181c2370c37b61454caf176654975b2f0b68c3f139c99aa802d6af79bd40e045000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000de00000000000000000000000000000000000000000000000000000000000000f4000000000000000000000000000000000000000000000000000000000000010a00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000136000000000000000000000000000000000000000000000000000000000000014c00000000000000000000000000000000000000000000000000000000000001620000000000000000000000000000000000000000000000000000000000000178000000000000000000000000000000000000000000000000000000000000018e00000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000001ba00000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e600000000000000000000000000000000000000000000000000000000000001fc00000000000000000000000000000000000000000000000000000000000002120000000000000000000000000000000000000000000000000000000000000228000000000000000000000000000000000000000000000000000000000000023e0000000000000000000000000000000000000000000000000000000000000254000000000000000000000000000000000000000000000000000000000000026a0000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000029600000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002ee0000000000000000000000000000000000000000000000000000000000000304000000000000000000000000000000000000000000000000000000000000031a00000000000000000000000000000000000000000000000000000000000003300000000000000000000000000000000000000000000000000000000000000346000000000000000000000000000000000000000000000000000000000000035c00000000000000000000000000000000000000000000000000000000000003720000000000000000000000000000000000000000000000000000000000000388000000000000000000000000000000000000000000000000000000000000039e00000000000000000000000000000000000000000000000000000000000003b400000000000000000000000000000000000000000000000000000000000003ca00000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f6000000000000000000000000000000000000000000000000000000000000040c00000000000000000000000000000000000000000000000000000000000004220000000000000000000000000000000000000000000000000000000000000438000000000000000000000000000000000000000000000000000000000000044e0000000000000000000000000000000000000000000000000000000000000464000000000000000000000000000000000000000000000000000000000000047a000000000000000000000000000000000000000000000000000000000000049000000000000000000000000000000000000000000000000000000000000004a600000000000000000000000000000000000000000000000000000000000004bc00000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004e800000000000000000000000000000000000000000000000000000000000004fe0000000000000000000000000000000000000000000000000000000000000514000000000000000000000000000000000000000000000000000000000000052a00000000000000000000000000000000000000000000000000000000000005400000000000000000000000000000000000000000000000000000000000000556000000000000000000000000000000000000000000000000000000000000056c0000000000000000000000000000000000000000000000000000000000000582000000000000000000000000000000000000000000000000000000000000059800000000000000000000000000000000000000000000000000000000000005ae00000000000000000000000000000000000000000000000000000000000005c400000000000000000000000000000000000000000000000000000000000005da00000000000000000000000000000000000000000000000000000000000005f00000000000000000000000000000000000000000000000000000000000000606000000000000000000000000000000000000000000000000000000000000061c00000000000000000000000000000000000000000000000000000000000006320000000000000000000000000000000000000000000000000000000000000648000000000000000000000000000000000000000000000000000000000000065e0000000000000000000000000000000000000000000000000000000000000674000000000000000000000000000000000000000000000000000000000000068a00000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000006b600000000000000000000000000000000000000000000000000000000000006cc00000000000000000000000000000000000000000000000000000000000006e200000000000000000000000000000000000000000000000000000000000006f8000000000000000000000000000000000000000000000000000000000000070e0000000000000000000000000000000000000000000000000000000000000724000000000000000000000000000000000000000000000000000000000000073a00000000000000000000000000000000000000000000000000000000000007500000000000000000000000000000000000000000000000000000000000000766000000000000000000000000000000000000000000000000000000000000077c000000000000000000000000000000000000000000000000000000000000079200000000000000000000000000000000000000000000000000000000000007a800000000000000000000000000000000000000000000000000000000000007be00000000000000000000000000000000000000000000000000000000000007d400000000000000000000000000000000000000000000000000000000000007ea00000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000816000000000000000000000000000000000000000000000000000000000000082c00000000000000000000000000000000000000000000000000000000000008420000000000000000000000000000000000000000000000000000000000000858000000000000000000000000000000000000000000000000000000000000086e0000000000000000000000000000000000000000000000000000000000000884000000000000000000000000000000000000000000000000000000000000089a00000000000000000000000000000000000000000000000000000000000008b000000000000000000000000000000000000000000000000000000000000008c600000000000000000000000000000000000000000000000000000000000008dc00000000000000000000000000000000000000000000000000000000000008f20000000000000000000000000000000000000000000000000000000000000908000000000000000000000000000000000000000000000000000000000000091e0000000000000000000000000000000000000000000000000000000000000934000000000000000000000000000000000000000000000000000000000000094a000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214f60b3f2721703fc99c21f9e7dc6a0a09dae7c7a97374616b650000000000000000000000000000000000000000000000000000000000000000000000000c3939393939393939393930300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010409069090072d95d3c60e38fc10087363c0eb47c19043af248f56929528d9c88eee605a1e19acc975ef519fac2434168e5e43554a782d5670215cc191cc81523125f404df1c3b73b1abaa2195fceecfcb63040fe20e92b8e440c6dfdc6ef3b0983ccc51d32c1a80f72ebe865be9e816ce9804e82a4ef302d4a5516b7104b31b0c48751b952c8d7d52cff70914cbc82623403530b1bacfb6d10bba4f19a6e15a2d1ac4d00622228cd65d3b3b7e0fbc965f949690976b364e74d71a504321cdef4f7921d4361edb4b47341cac9e67ccd956b96552947f34cc7426fb9660e3806fe31e72b3bc0aab61f4cfe096b3587be6dfe1e6aefca8528b6047c0fe6bea0b7d1b8278a9a700000000000000000000000000000000000000000000000000000000" +} diff --git a/test/sp1-ics07/fixtures/membership_25-plonk_fixture.json b/test/sp1-ics07/fixtures/membership_25-plonk_fixture.json new file mode 100644 index 000000000..87bc6e60 --- /dev/null +++ b/test/sp1-ics07/fixtures/membership_25-plonk_fixture.json @@ -0,0 +1,10 @@ +{ + "trustedClientState": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000012754500000000000000000000000000000000000000000000000000000000001baf8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000673696d642d310000000000000000000000000000000000000000000000000000", + "trustedConsensusState": "00000000000000000000000000000000000000000000000000000000674906bf9a91aa3a5994d67c5b207e59babf294a68d9154ba027ecf414f9cf1fe82c1d281e4ffa9ddd5f7686c4fc2aeafdec2d2bb819ce246c591d7e6fe2334d38d6f3c3", + "updateClientVkey": "0x00ceb6e478b4abf450ee24d278ede0baf5568fd906b2c9f128cfa7eb83c2e225", + "membershipVkey": "0x007c90a390038b52fa1dd0d1b8e91bc7905dc81bb46274664c1bd41ce871b3f7", + "ucAndMembershipVkey": "0x006948fde1a874707ef63d93392cf0575860b605e972f5df2bf4d49dbb711d1c", + "misbehaviourVkey": "0x001df6ba765a3648a4b529447837712d6b6d3f4c72e9c9394c987b0b15b7a154", + "proofHeight": "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000f", + "membershipProof": "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000674906bf9a91aa3a5994d67c5b207e59babf294a68d9154ba027ecf414f9cf1fe82c1d281e4ffa9ddd5f7686c4fc2aeafdec2d2bb819ce246c591d7e6fe2334d38d6f3c3007c90a390038b52fa1dd0d1b8e91bc7905dc81bb46274664c1bd41ce871b3f700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000209a91aa3a5994d67c5b207e59babf294a68d9154ba027ecf414f9cf1fe82c1d28000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000005e0000000000000000000000000000000000000000000000000000000000000074000000000000000000000000000000000000000000000000000000000000008a00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b600000000000000000000000000000000000000000000000000000000000000cc00000000000000000000000000000000000000000000000000000000000000e200000000000000000000000000000000000000000000000000000000000000f8000000000000000000000000000000000000000000000000000000000000010e0000000000000000000000000000000000000000000000000000000000000124000000000000000000000000000000000000000000000000000000000000013a00000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000166000000000000000000000000000000000000000000000000000000000000017c000000000000000000000000000000000000000000000000000000000000019200000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000001be00000000000000000000000000000000000000000000000000000000000001d400000000000000000000000000000000000000000000000000000000000001ea00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000216000000000000000000000000000000000000000000000000000000000000022c0000000000000000000000000000000000000000000000000000000000000242000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c393939393939393939393735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000462616e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0214a4f219aa168e9d636a6ac8ef5521e7744e9b91a07374616b650000000000000000000000000000000000000000000000000000000000000000000000000c3939393939393939393937350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036454bdcae30904adabd5e9e5e101ed3efc2d3e0a3cb84386398740b56b28a189381202a1b9271eebdd6e4639c7f74b5c99cb61d41aa75f3ac4db9459d9f2add7a9a8dd271f084b1d020c4d4b583fab654deefb44e5ccdb3fd1dfb864e560a8b3473c07378c09b735b79a43eebaa2e2826ad60e32dc95588fa2e391aed331f99cb2bf6dcbd82c9af046fd8d0c58ea205f2d6428c91242587ba28567e4f725a380879703d478272e62eae6f360223713a656e79ad4b05e581323784f4b7aa15ed74d12b00fcb01f82e6f8864a34220ab74a097d463fa6603cf6aeb956a9e8421c4d73e24c93d230bbc10de1936c9a76c62261535c5e5ef18ef62c6959432f8dfa9ef6df9a0ce20a4eb57bc3266affc530ee1e996e5b14dc2248b0e7c09572af7e5f0cc4b04c513ed71533ac6146c42aa6ceeaf04658892260c3c192ca7e0153eefc4f8a82b242bb8149456b1f82b498c1bfe26f3b13e534015a2c42f033f233e390a81d6a18c1c5ce5cd9704265ec4db35adf8bf8aef4c9521f2a3c5a9beded028c8435a3c2408fea115f28cd6586832606c30c7e5ed832ea6ec0f7fabec62098aa9a1576afb222dc8964de1c897156df126fd80ad9ecbd8fd0cf13413c665d2c3263ed5402e19690155b4b0631d089da2fcb4970b2409266ab35faf8d82eb01b562c32b38030f7a5f6d48e7d9eef25867ec1277c5a52964bd8e381257acd2c09b771ddec43e12e850ae36599f7307b4000d7fa8a6fa6ed4c458213dfb25260879d8907640042c64a2436ad3f330ed2a9dfbb3bd18c3509b4d537e3f0233c39077ad35f554cd141fbf116260350537f6615280c1d687d13421eb9fe21292c4f4cf9fc3ee57ee2311ff944e11d0217b3f3df154fd8db2f8d971cba0a88efa910a9977231d9b9c19d8d3279ecb3cc97bd3f77846d84fee5ab1f5a3872d6e5ca4d5a4a6f51e355b2fdd28ca3ee078578b0950350889049df14c355fb9670b038de47ff49d13db8411d4ad50af0e18e2018bc3c2b9363fc0885c3c974baa306cd4f52fea4011e8a011b4b6e151bb6c97c3b74ba89027861e7a30f0f6545f923b2d2e5c22948ad5f12bb7d6213bc59cd990feaa668d59bec53abc23f0ca2c6cd979342b7c639424f922af84b4e4696ab563e7d0f0f7aa130b7bdcf116f2b6e4bc6db8325c8a4f68af0a1cc3df90282af414245a6b0b64fff403ab2bc577e8a710295df154fc6f62bf00000000000000000000000000000000000000000000000000000000" +} diff --git a/test/sp1-ics07/fixtures/memberships_fixture-groth16.json b/test/sp1-ics07/fixtures/memberships_fixture-groth16.json new file mode 100644 index 000000000..602e261b --- /dev/null +++ b/test/sp1-ics07/fixtures/memberships_fixture-groth16.json @@ -0,0 +1,10 @@ +{ + "trustedClientState": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf3000000000000000000000000000000000000000000000000000000000012750000000000000000000000000000000000000000000000000000000000001baf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076d6f6368612d3400000000000000000000000000000000000000000000000000", + "trustedConsensusState": "00000000000000000000000000000000000000000000000000000000674900707cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b70113b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638", + "updateClientVkey": "0x0023574eac18066df268dea9a9bd84ec88a028723b7b5bfc0157c3fb6223d3dc", + "membershipVkey": "0x00edc4e20439e0bb590e694cfb896d45b4e0a0f415d5996454b20247e675a8a9", + "ucAndMembershipVkey": "0x0063acab8b50788d68ec2c39853b288c760d331e326134cd662466d20e813cd3", + "misbehaviourVkey": "0x0060a255f3b9211fd92f0d8a6d3a6c7f1181e22eefc3b3ae65efa828d97a9eda", + "proofHeight": "00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf3", + "membershipProof": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000674900707cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b70113b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec6263800edc4e20439e0bb590e694cfb896d45b4e0a0f415d5996454b20247e675a8a9000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000004c0000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000207cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b7011000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000369626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023636c69656e74732f30372d74656e6465726d696e742d302f636c69656e745374617465000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b70a2b2f6962632e6c69676874636c69656e74732e74656e6465726d696e742e76312e436c69656e7453746174651287010a1174686574612d746573746e65742d3030311204080110031a040880840722040880c60a2a02082832003a0510b7e3c60842190a090801180120012a0100120c0a02000110211804200c300142190a090801180120012a0100120c0a02000110201801200130014a07757067726164654a10757067726164656449424353746174655001580100000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000369626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025636c69656e74732f30372d74656e6465726d696e742d3030312f636c69656e7453746174650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010409069090304fa2d588b2c0f867046b6b7fd7cdf697ecb45dd4ee027f16e4e7ae4b2376701e547faad4475100ee2580ee07a78a3afe773610549e82ffed3b96ccd2f89a2a11e920bcad88b0581f7fecc94f22e9b01eed4d8d19ab385c795de25313382f492d60f19d5115da0885ec64b287d49fb9c907b862b55bbafe8a424d4d59139835143c44a80b1500d928db9ff91b0a8a17302872625f0ba9121bb8d69430d7394d0894520181d1765c1def056d4381fe0b0daef10c3c8f8fbb4e84ce013701876a250fe1bf7e4000f74bd7af512892a86a0dfc5bf281cb125745b7704991f3ea811114f5aff1846a958da5796ecf44d46b873928dcc3f981f81c6848a5e963d6f800000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/test/sp1-ics07/fixtures/memberships_fixture-plonk.json b/test/sp1-ics07/fixtures/memberships_fixture-plonk.json new file mode 100644 index 000000000..939ed5f0 --- /dev/null +++ b/test/sp1-ics07/fixtures/memberships_fixture-plonk.json @@ -0,0 +1,10 @@ +{ + "trustedClientState": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf3000000000000000000000000000000000000000000000000000000000012750000000000000000000000000000000000000000000000000000000000001baf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000076d6f6368612d3400000000000000000000000000000000000000000000000000", + "trustedConsensusState": "00000000000000000000000000000000000000000000000000000000674900707cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b70113b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638", + "updateClientVkey": "0x0023574eac18066df268dea9a9bd84ec88a028723b7b5bfc0157c3fb6223d3dc", + "membershipVkey": "0x00edc4e20439e0bb590e694cfb896d45b4e0a0f415d5996454b20247e675a8a9", + "ucAndMembershipVkey": "0x0063acab8b50788d68ec2c39853b288c760d331e326134cd662466d20e813cd3", + "misbehaviourVkey": "0x0060a255f3b9211fd92f0d8a6d3a6c7f1181e22eefc3b3ae65efa828d97a9eda", + "proofHeight": "00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf3", + "membershipProof": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000674900707cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b70113b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec6263800edc4e20439e0bb590e694cfb896d45b4e0a0f415d5996454b20247e675a8a9000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000004c0000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000207cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b7011000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000369626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023636c69656e74732f30372d74656e6465726d696e742d302f636c69656e745374617465000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b70a2b2f6962632e6c69676874636c69656e74732e74656e6465726d696e742e76312e436c69656e7453746174651287010a1174686574612d746573746e65742d3030311204080110031a040880840722040880c60a2a02082832003a0510b7e3c60842190a090801180120012a0100120c0a02000110211804200c300142190a090801180120012a0100120c0a02000110201801200130014a07757067726164654a10757067726164656449424353746174655001580100000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000369626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025636c69656e74732f30372d74656e6465726d696e742d3030312f636c69656e7453746174650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036454bdcae30a9c505076f592751dca03cbbe0c74a78a0de2c33d647f0dd062a7614a531dea169a75d1a0053556cd301b3520ffe525c4ef25596fb0eb2ae8dcfcd24fb1cd4910dcf4f995735e7a297aec27f070854ef130b845ba3b5843d9af7ef72c40cda7191a5906442b1ba9b3392670c31267c533cc1a22eb1510cd3fdcbc3d3d1f18491865c77402e8d525a940958950c2ca1bbc4c3d8050e90b2e72f8546f97c5bac602526ad3842ac2decd67b13432716770d5b158211059d220bb34e9ef1ef329d31dfef2ec297d6866d26112a5893eedad86206cc3fc886e28500b502152eb60a31794892294050e07899f194cc959faad6dc852839e34a6d6cfc4d24b471d57b3252d352c0498aa1d9d326fae25e9cc016f9de74af4b0d6293a59cf0f3cd5c5aa30057f3a886849722e8f76879d97fc80385ac0782f386324d91811a236b6ccbf210786a7a31f6b30f4538c5a8c0de5ac5450f2f45228faf764ae27082f59763402e6a31f3802ccd354d22b4b97b273ca9bd012d0fc14c791be9083f52e5e9db106ca1e785042c78f7ed63519bc0768bfc2e2d965321e5ca7f070461d3bcb1b771a400a3125618dfda693f626b5cae9f5c080dba7240a581bd9b6a30e0154718814488715e746b133eff111966ad4c99c5bae9fe4a4a20618b4d22c124f29c86d0f77ad9ca59155961d9dbfdba6d884f6626613c2251cdeaf5735b1f0b079c13202480a1f799d9a1d179a7a31d51a63ead623c55bbc2cc553cdfe658471b9ae7b17af468c6a77cc98fd26f5a00d35e46d25ac2c68f0676c40fe564e7191fee99d1673ad6f643907cd2f2b1ed17268964d71568bfdfbceca9407cc7453bf12208c2ec21ae71b828831fbe4b978bc7298940cd985a70dad91a7219d8cb27245950f2938906959d68e7ab3582564f48bef7b1d3311657b7ae815ec93eb52ef2c9fe21d961b3337a10cbc8d8f2a720dc599fa70b979630a9d60a58edd9a3bebc77d4b12919c2c35184cd3ef77d8c5e95b8a84483f092b61f36b7c8613863efdf4d37e2936a2b2c11234b847caf9f04c8d88c5c739ce2fae17fa87792d2eb8619acab40a8ee4f18260df1bcfaea0740c6c39b4730359b3db386f10d3228c3c119a605e1cc764aeb4938c4557a88dc4ee2c082b1fe8dc1bacd408dac999c268b20bbd6a24a3e7cfde0e7dfdacca261db01980eb24e42fd1a286f10e93072e7c0a3a9e7200000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/test/sp1-ics07/fixtures/misbehaviour_breaking_time_monotonicity-groth16_fixture.json b/test/sp1-ics07/fixtures/misbehaviour_breaking_time_monotonicity-groth16_fixture.json new file mode 100644 index 000000000..0b4bc61e --- /dev/null +++ b/test/sp1-ics07/fixtures/misbehaviour_breaking_time_monotonicity-groth16_fixture.json @@ -0,0 +1,9 @@ +{ + "trustedClientState": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000012754500000000000000000000000000000000000000000000000000000000001baf8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000673696d642d310000000000000000000000000000000000000000000000000000", + "trustedConsensusState": "000000000000000000000000000000000000000000000000000000006749043a14c2c457224e612ed674be942343df77086a41bc135299911dc64c7c8aa98c433956df6b408ddd4df1fb35590eb9a23730b671a7bdf7e0d4772837f1bc91851b", + "updateClientVkey": "0x00ceb6e478b4abf450ee24d278ede0baf5568fd906b2c9f128cfa7eb83c2e225", + "membershipVkey": "0x007c90a390038b52fa1dd0d1b8e91bc7905dc81bb46274664c1bd41ce871b3f7", + "ucAndMembershipVkey": "0x006948fde1a874707ef63d93392cf0575860b605e972f5df2bf4d49dbb711d1c", + "misbehaviourVkey": "0x001df6ba765a3648a4b529447837712d6b6d3f4c72e9c9394c987b0b15b7a154", + "submitMsg": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020001df6ba765a3648a4b529447837712d6b6d3f4c72e9c9394c987b0b15b7a15400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000006749044b0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000006749043a14c2c457224e612ed674be942343df77086a41bc135299911dc64c7c8aa98c433956df6b408ddd4df1fb35590eb9a23730b671a7bdf7e0d4772837f1bc91851b000000000000000000000000000000000000000000000000000000006749043a14c2c457224e612ed674be942343df77086a41bc135299911dc64c7c8aa98c433956df6b408ddd4df1fb35590eb9a23730b671a7bdf7e0d4772837f1bc91851b00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000012754500000000000000000000000000000000000000000000000000000000001baf8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000673696d642d3100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104090690900a6a92d5ddc6ea18c7636d3ae5aa1b6f73504a2d0745c5277eadec7564104bca1cb31aa532188fac5ed2cc92bb2bc005271544eb6eb7ecfc3913f15027a0d1a72ff54159a4cb1ba757781f8fb0d8eba9587108fa054e0b08a8c43bea239d444d10f8f93b081565cf061dfa7e92daa6e6be4db1c3ebea1cba68dca03ff5b69558096a92df894510dd0256272b3b3d9a7029b4509aa6c63c4a299bfed5f85c091924c655dd53256aa3903a5327b5332d41e869f7037ac4a8b9503b542e389e074110f771a3e83e9cea7c19a6de4f04c488f4ae0752fab9438147915e14cc4dbd6d2a966fdab954c4dd9832dd0fd82cc609fc54a0759362bd7f16ab7dc6b7b08f6100000000000000000000000000000000000000000000000000000000" +} diff --git a/test/sp1-ics07/fixtures/misbehaviour_double_sign-plonk_fixture.json b/test/sp1-ics07/fixtures/misbehaviour_double_sign-plonk_fixture.json new file mode 100644 index 000000000..166c005c --- /dev/null +++ b/test/sp1-ics07/fixtures/misbehaviour_double_sign-plonk_fixture.json @@ -0,0 +1,9 @@ +{ + "trustedClientState": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000012754500000000000000000000000000000000000000000000000000000000001baf8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000673696d642d310000000000000000000000000000000000000000000000000000", + "trustedConsensusState": "00000000000000000000000000000000000000000000000000000000674903906796c450947a65adb894506e204645746d51aff805229f07b2f05705366c0d2ec1c2922e9ed1f6789f3841c8ae03bfd99e91b454ee4366ddf9e9064823c0d417", + "updateClientVkey": "0x00ceb6e478b4abf450ee24d278ede0baf5568fd906b2c9f128cfa7eb83c2e225", + "membershipVkey": "0x007c90a390038b52fa1dd0d1b8e91bc7905dc81bb46274664c1bd41ce871b3f7", + "ucAndMembershipVkey": "0x006948fde1a874707ef63d93392cf0575860b605e972f5df2bf4d49dbb711d1c", + "misbehaviourVkey": "0x001df6ba765a3648a4b529447837712d6b6d3f4c72e9c9394c987b0b15b7a154", + "submitMsg": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020001df6ba765a3648a4b529447837712d6b6d3f4c72e9c9394c987b0b15b7a1540000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000674903aa000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000674903906796c450947a65adb894506e204645746d51aff805229f07b2f05705366c0d2ec1c2922e9ed1f6789f3841c8ae03bfd99e91b454ee4366ddf9e9064823c0d41700000000000000000000000000000000000000000000000000000000674903906796c450947a65adb894506e204645746d51aff805229f07b2f05705366c0d2ec1c2922e9ed1f6789f3841c8ae03bfd99e91b454ee4366ddf9e9064823c0d41700000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000012754500000000000000000000000000000000000000000000000000000000001baf8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000673696d642d310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036454bdcae31c4ce0c8631d43c97c9e8b56701ce53db868621ef09ee7557172caf0daebaf211018ff19db6a6d1c39391f6d0efc929bd00329d265878a9007ba735d2ff757c32a40d1a0bc2ad4063a81e4a978538bb4ce23005c15a7fd800c37cb6e9294a53e03eb085f959c0c24a8cd4a1374ee786214f230759652b24e8f3aaece03d871962bc5d7e27e0c746c7fba190d97d4a80750cd3851b9cbc96f9c14925e2892b7cd24e98d7e8540bdca1d2e473be24f563e79e2e4bfd63b884339c210cc1d5b8a752f6799f335339e2aff14cfb99a575ac40643e7df6aa97048465ac9ba31627a882bc398fd519eb508e935c6aee4d4fca853339a7826819ddcade76601017792380ff7fefe90221c7823f92d47707c9cbac3817e3d342f655d51a650317f9d99e70f5afb8039281f8e0dafe936fc3ddbf46cde5560c83f5f480e911547f869225b1b47df1c9285fc89c9575a03dbea2d5665c0bb309e3369e2b4d6d3205c76544501e1926e9454d1d6c6d93791bd8f1ddc11057a19b1e008dbcd5e133b175c4708061f28ae7274b5de4674968a0f1b12243658e659c40997b1b2f93182fdf3a1ec22fea6dec448af2053e539ab3236955e2b7275975c5bfd2055d4488bb56dc340135400d6b832db87a57af223c623d063c00a962bfd211ad10daac81017d8ea402ddd3509fda5d9c0018200b59d721167e1298345f42b85bc37e52e5de6234f4711bc6edbc8fa43bf7d76184b817761acdbc277226341dd29097427180ae0d1a51f399c18575373d14da38bd7f4d704bd17321e172bf84d8c5c268597890026ae0f59d991f8d74efec7461f0d789fa90439e8e63c203255cfa85f6c96fe3dc16c276be7994a37cae191a1dd04917b4caca1ab249d639b283b29f2d6c1885c3cc32e030cbd21c400661b30d09527299be25f9f4f1ea2c3f939aceb595e333a9cb42286cc56ba561436bb6d026d2f505af7d24a7efb09e44f74e529a678443406f911a13898f363648497682ace646fba6459e6a4aba2c9d38a5609cfdcfaefb2340d750d92d6021d41009671c39e97c7c2eb2d27cd01f6f160afb05aa3f9b0901719f69f8d23b2843d0b50d5855740e8d20578f56aa5658952f6901a3ce57b56e1170623ebbac65264c433f77cc3bc740d28a8c4a906029bc3ff540de08e830151022090c00f547483fcc2d7f6174fc17ae60e2b3d7dea83746cae8c93d627fe1500000000000000000000000000000000000000000000000000000000" +} diff --git a/test/sp1-ics07/fixtures/uc_and_memberships_fixture-groth16.json b/test/sp1-ics07/fixtures/uc_and_memberships_fixture-groth16.json new file mode 100644 index 000000000..4616cecb --- /dev/null +++ b/test/sp1-ics07/fixtures/uc_and_memberships_fixture-groth16.json @@ -0,0 +1,10 @@ +{ + "trustedClientState": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf3000000000000000000000000000000000000000000000000000000000012750000000000000000000000000000000000000000000000000000000000001baf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076d6f6368612d3400000000000000000000000000000000000000000000000000", + "trustedConsensusState": "00000000000000000000000000000000000000000000000000000000674900707cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b70113b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638", + "updateClientVkey": "0x0023574eac18066df268dea9a9bd84ec88a028723b7b5bfc0157c3fb6223d3dc", + "membershipVkey": "0x00edc4e20439e0bb590e694cfb896d45b4e0a0f415d5996454b20247e675a8a9", + "ucAndMembershipVkey": "0x0063acab8b50788d68ec2c39853b288c760d331e326134cd662466d20e813cd3", + "misbehaviourVkey": "0x0060a255f3b9211fd92f0d8a6d3a6c7f1181e22eefc3b3ae65efa828d97a9eda", + "proofHeight": "00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338c4d", + "membershipProof": "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000920000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200063acab8b50788d68ec2c39853b288c760d331e326134cd662466d20e813cd3000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000007a00000000000000000000000000000000000000000000000000000000000000720000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000674900707cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b70113b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638000000000000000000000000000000000000000000000000000000006749023875dd6984630b3dc17094cdb1552997558160c12bd1a4c3caeec0d28d68e75e5a3b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec6263800000000000000000000000000000000000000000000000000000000674902b700000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338c4d00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf3000000000000000000000000000000000000000000000000000000000012750000000000000000000000000000000000000000000000000000000000001baf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076d6f6368612d340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000369626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023636c69656e74732f30372d74656e6465726d696e742d302f636c69656e745374617465000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b70a2b2f6962632e6c69676874636c69656e74732e74656e6465726d696e742e76312e436c69656e7453746174651287010a1174686574612d746573746e65742d3030311204080110031a040880840722040880c60a2a02082832003a0510b7e3c60842190a090801180120012a0100120c0a02000110211804200c300142190a090801180120012a0100120c0a02000110201801200130014a07757067726164654a10757067726164656449424353746174655001580100000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000369626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025636c69656e74732f30372d74656e6465726d696e742d3030312f636c69656e74537461746500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000104090690902eca7cb1fbc3ca45afec736e9e6ac40d393e4a559e2282d35d065756b59e6bc62582b0a5eb2abbf3a9b2ce90383826cb255d29ba09dcdbcbcff0cc7596bfce4b142229c271c830ab24b24ad3db5c43f76d5ef57a81278efe5e95f50cd88e53440b5bc82bbba713e902cdb497faf4e39c8751be6d16f961bfb5106aea3683e71726bea6c0e0445b472d82771102d1ed412efdde7bd7ccd551528cb57204e68849265cfe302551f2caf570a9ccdec74fcb070feacba714956b0271f0f0ba8c452227e9479e06b59deda5cbc2c62153c3f09b7817578478147749508346c7db4e620e44cae86210b7df6f859e1958db57e729bb73999b095b0be2af3b0d7927db8a00000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/test/sp1-ics07/fixtures/uc_and_memberships_fixture-plonk.json b/test/sp1-ics07/fixtures/uc_and_memberships_fixture-plonk.json new file mode 100644 index 000000000..eb68b04d --- /dev/null +++ b/test/sp1-ics07/fixtures/uc_and_memberships_fixture-plonk.json @@ -0,0 +1,10 @@ +{ + "trustedClientState": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf3000000000000000000000000000000000000000000000000000000000012750000000000000000000000000000000000000000000000000000000000001baf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000076d6f6368612d3400000000000000000000000000000000000000000000000000", + "trustedConsensusState": "00000000000000000000000000000000000000000000000000000000674900707cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b70113b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638", + "updateClientVkey": "0x0023574eac18066df268dea9a9bd84ec88a028723b7b5bfc0157c3fb6223d3dc", + "membershipVkey": "0x00edc4e20439e0bb590e694cfb896d45b4e0a0f415d5996454b20247e675a8a9", + "ucAndMembershipVkey": "0x0063acab8b50788d68ec2c39853b288c760d331e326134cd662466d20e813cd3", + "misbehaviourVkey": "0x0060a255f3b9211fd92f0d8a6d3a6c7f1181e22eefc3b3ae65efa828d97a9eda", + "proofHeight": "00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338c4d", + "membershipProof": "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000b80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200063acab8b50788d68ec2c39853b288c760d331e326134cd662466d20e813cd3000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000007a00000000000000000000000000000000000000000000000000000000000000720000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000674900707cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b70113b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638000000000000000000000000000000000000000000000000000000006749023875dd6984630b3dc17094cdb1552997558160c12bd1a4c3caeec0d28d68e75e5a3b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec6263800000000000000000000000000000000000000000000000000000000674902a300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338c4d00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf3000000000000000000000000000000000000000000000000000000000012750000000000000000000000000000000000000000000000000000000000001baf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000076d6f6368612d340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000369626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023636c69656e74732f30372d74656e6465726d696e742d302f636c69656e745374617465000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b70a2b2f6962632e6c69676874636c69656e74732e74656e6465726d696e742e76312e436c69656e7453746174651287010a1174686574612d746573746e65742d3030311204080110031a040880840722040880c60a2a02082832003a0510b7e3c60842190a090801180120012a0100120c0a02000110211804200c300142190a090801180120012a0100120c0a02000110201801200130014a07757067726164654a10757067726164656449424353746174655001580100000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000369626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025636c69656e74732f30372d74656e6465726d696e742d3030312f636c69656e7453746174650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036454bdcae32f46e5ba7abd334232b6f1e7d01fe688dd948129394f74df2dbab3b58d109b5f01f4e3be04de75dc43feb62e01a48dbac3de23fe71e159c8d3047835bbc5536e05dbc5b8faf6b9f58b9c8752871a90e70c7d00cdc87b9fefc3082371bc88489f0b141a0c967e709398079ee4fdd588e7b9a3ddfdbb9fe02604e9e5eaad7600610529e5d11a8b7c7de36c82d2945459dc5c0ca419b2173bccf2382388468b43420bdbb51aebcd6820906562e3bdbc9ff11b0ff0c6e4bafe190cdcf6e61143c4162d6e82896a648177cefaed531759939d52e4d2f0a7e425db7568e3a43afe89770edb5db7c38cea7eda0e59630f6afcb49aeb0e42ae63cc1fdd80898e022d7dfe0fffced240aefc39eb3fa6bc5d849a0ea37b19ee918d1654b28bc216a849901023e6d378c4413f16c448e28b64a2de80a950e892aba3eb9b0a38ba1fcf56e7bc0e15e2f736a867a302febb84c89a4d04eaba6c28a46bdc3cb339f79f867e0ca11bcc67e2428a3b26aad8d53b085e55562759139aca70048f17c1e7e4c41b390127d0c928eed14e2f32fdedb3788653e4ff2e24ba53321001d2e74f3ea681123b1f802e2dfac4b5ad26987daa6fbaf4d1e1b2ba48634d3e6a21568c89a645af520689475b205ce54151bce39571541796c8a0ef218f6e2948bf24932395a2889605cdee683e509ba4a35cc6f15d2a64707a70f3d70207a6a7da2744ad4178730611e50287ba12cf2f047cea01477f0489b38989090b89b1bdc5766e28945c4d97270dfd073b5c145491f516a064ffa7480a479c09ed18ec54afebcea8fd0d6b9e01221c2b9f2fff271b57dfd51140e257347fe72173a308e3e79f5cf0e3c784a12c3ceb2b0b429eb9bca9f360339016c7975f19dd54635862fdfe1f97cef039d3028fd78a9aa49d036cfb218cb4f1bca2844adb3726c8773f454e20340d7c6f7b14e3f98586f981198dfb89bc5f9787beddf8480c972c1e9bfc60f77802d4505e13e45485683e42100b4f88611eed3ac6e5aeccfe3723600130084514ad0f9407160c9f21d2c96d265fa0c968303f934ab265c6abb7f5fc28750a44614d87d6cb18866e357a7d458ca61f39566be7b7045f4979f2bbd043eeb1c54451267f3f6228e5dd9b2b6ad8e4251966d1e16b02e6df593b43e4849aa96c552e98bf07dab31fc95de095780c66a58fddb9c8a7ece01bb7b9b9d2b77a496fdb3b777c01aa8600000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/test/sp1-ics07/fixtures/update_client_fixture-groth16.json b/test/sp1-ics07/fixtures/update_client_fixture-groth16.json new file mode 100644 index 000000000..520ca7a9 --- /dev/null +++ b/test/sp1-ics07/fixtures/update_client_fixture-groth16.json @@ -0,0 +1,11 @@ +{ + "trustedClientState": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf3000000000000000000000000000000000000000000000000000000000012750000000000000000000000000000000000000000000000000000000000001baf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076d6f6368612d3400000000000000000000000000000000000000000000000000", + "trustedConsensusState": "00000000000000000000000000000000000000000000000000000000674900707cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b70113b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638", + "updateClientVkey": "0x0023574eac18066df268dea9a9bd84ec88a028723b7b5bfc0157c3fb6223d3dc", + "membershipVkey": "0x00edc4e20439e0bb590e694cfb896d45b4e0a0f415d5996454b20247e675a8a9", + "ucAndMembershipVkey": "0x0063acab8b50788d68ec2c39853b288c760d331e326134cd662466d20e813cd3", + "misbehaviourVkey": "0x0060a255f3b9211fd92f0d8a6d3a6c7f1181e22eefc3b3ae65efa828d97a9eda", + "targetConsensusState": "000000000000000000000000000000000000000000000000000000006749023875dd6984630b3dc17094cdb1552997558160c12bd1a4c3caeec0d28d68e75e5a3b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638", + "targetHeight": 3378253, + "updateMsg": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200023574eac18066df268dea9a9bd84ec88a028723b7b5bfc0157c3fb6223d3dc0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000674900707cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b70113b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638000000000000000000000000000000000000000000000000000000006749023875dd6984630b3dc17094cdb1552997558160c12bd1a4c3caeec0d28d68e75e5a3b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638000000000000000000000000000000000000000000000000000000006749028f00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338c4d00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf3000000000000000000000000000000000000000000000000000000000012750000000000000000000000000000000000000000000000000000000000001baf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076d6f6368612d340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001040906909029f6b8fab698ad6a243e1cb4984a0b40002cd5a69077b38a011ba577e8e058a119bb740a8c3c35c156602b53d0e34a1e95cd30005ee830ec83d85fc0a0c7e82226eaf7cac1c396f57c80bfc534da3428325013594be99850c20c05795f6fb523127a8ebeb9ee3958c6eb4e2cf7ac8c2ccf9946ef68f919a0fa4b3a458ebab1410c71d93439de07ab248e72b7bd7ae2e84cb96342df6c5b1c62e2e9f5d748327419c32dfae8fd2bcfe09f7cd64ee44ee2779bfdff4fbc2dab5b373ddcd83ae2be046b428b20035e92f81a81ef2ace8655ec42e487797e61cdc56c0e16e2569be103daf047cdcaee53ff15f330c8cc5a960b0eafb4952f5c1773eff8e18493dc3000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/test/sp1-ics07/fixtures/update_client_fixture-plonk.json b/test/sp1-ics07/fixtures/update_client_fixture-plonk.json new file mode 100644 index 000000000..e3413448 --- /dev/null +++ b/test/sp1-ics07/fixtures/update_client_fixture-plonk.json @@ -0,0 +1,11 @@ +{ + "trustedClientState": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf3000000000000000000000000000000000000000000000000000000000012750000000000000000000000000000000000000000000000000000000000001baf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000076d6f6368612d3400000000000000000000000000000000000000000000000000", + "trustedConsensusState": "00000000000000000000000000000000000000000000000000000000674900707cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b70113b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638", + "updateClientVkey": "0x0023574eac18066df268dea9a9bd84ec88a028723b7b5bfc0157c3fb6223d3dc", + "membershipVkey": "0x00edc4e20439e0bb590e694cfb896d45b4e0a0f415d5996454b20247e675a8a9", + "ucAndMembershipVkey": "0x0063acab8b50788d68ec2c39853b288c760d331e326134cd662466d20e813cd3", + "misbehaviourVkey": "0x0060a255f3b9211fd92f0d8a6d3a6c7f1181e22eefc3b3ae65efa828d97a9eda", + "targetConsensusState": "000000000000000000000000000000000000000000000000000000006749023875dd6984630b3dc17094cdb1552997558160c12bd1a4c3caeec0d28d68e75e5a3b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638", + "targetHeight": 3378253, + "updateMsg": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200023574eac18066df268dea9a9bd84ec88a028723b7b5bfc0157c3fb6223d3dc0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000674900707cda849e141d0ba6c3dec0065cc7cca39890e6c082f8ce2eccc8410c415b70113b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638000000000000000000000000000000000000000000000000000000006749023875dd6984630b3dc17094cdb1552997558160c12bd1a4c3caeec0d28d68e75e5a3b3975cf0abdc1de0103aa5644a943f5690a83fafd90ce8909901354cec62638000000000000000000000000000000000000000000000000000000006749027b00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338c4d00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000338bf3000000000000000000000000000000000000000000000000000000000012750000000000000000000000000000000000000000000000000000000000001baf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000076d6f6368612d3400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036454bdcae3188b742e2205888a7a1bce8ee05cc395bd2eac6667f92f0c1a5a740b1348ffef1cfc02e1381c7922c2de519857b901068521152bbdf4df17fec60b87fb244b202580e20ca5eea3addea4fc4abd58a30542fb63c3c070940fcad16230b993a0e604d7d3289737f4a0de7a13dc661c16ec1bb3faade9a35cc1541210f3309c20e4208d5b69cbb3ae7ffb3cbf1614fe1d2e47a870bf7cd6a724ac22f436724bf0fb065051abb92da1ad112162236f0b35b84d54becf1a4371507e1e03d45cdda20927a55d94623a97d5f52096693d215adf159b32c75a95f9feb6265f9f94e396aa2b8e885c2b87cb88911efc8361cb74b91a707daf9d73d9a18cbcbfcea6256dbb0d53579e0f188161fba77acb056e9418e5d28ed7c21e224466e487baf099acbb14e5b2376583002d63239af6300cf86b14747c1aa56a3d5fa93d6c28462a1df52cfba62ca6ba008b5778e05cef4de6c99faba76575f9142b212902ccbe8e9724276dd4143f8facceffc7cb2481f90359391d064c3748b7b2aaf4487488921f712eb2a5655874ae14ded3ba40f1549881fea8b916d6cd4f433c10bf7f98545a6517a32322b70ebe62b575184b803091b8464119868f29bfd4df722722d8a1abe527233c989306910fd772c8a53f7edc59d2280364c0566cd392bed577cad2f1a70c41df726b3f8bf93c05aa05e268f25712788771f3adc033269143c753b442a001261bbbbeeffcd29a9fd577c45584aec8f6abdda1bfa7da6ec8eee5ca12870515c662d04b4016a0ebeaeaa5060666378f3b04d8407a1c9ea50456e37c4ee0491f3e4250bbd2e6c1d21d277e50ad9abe91282d3853bcdd6e82a9caf6aaf639d6218237bb2430893e56f8ca446daf86d53b4ca44fafc6769b8e67d63729919b7525b1e1cad52212f4e0cdf0130eb3c7d6a18cf9b8f7e07d957f615e9965790ae210a8728092be50c1b0090c72794d18ddd6ad9efca8f72df2d805946e5ee322532710f3c7a9df964f1dea9d41f8dc0b458dd296a860cc2429f9594390b1b588260ff1b45d7422b936345de5f75ff841d8349bb5e23756435589cefa6efea38efd1b3fb51d765ec664e021fb112f10dfa7e96be151b55a606af8295ebe1bf8cce7200ad024b7e8a2aa07e7e7e7a477ef88aef643d06a4d4699402b4eb625d9a11d17e9e19f8ea825a2fc62bd2d76c3d7f35a6c9a6d34a48abc5e493015d86cb2a500000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file