From ece2be507a8a3852f9496d06ae2c53939da9e874 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 23 Nov 2023 10:23:06 +0000 Subject: [PATCH] Cosmos / CosmWasm agents (#2865) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --------- Co-authored-by: Mattie Conover Co-authored-by: hashableric Co-authored-by: byeongsu-hong Co-authored-by: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Co-authored-by: Nam Chu Hoai Co-authored-by: Yorke Rhodes --- .github/workflows/agent-release-artifacts.yml | 8 +- .github/workflows/e2e.yml | 7 +- .github/workflows/mergify.yml.bak | 6 +- .github/workflows/monorepo-docker.yml | 4 +- .github/workflows/node.yml | 14 +- .github/workflows/release.yml | 2 +- .github/workflows/rust-docker.yml | 4 +- .github/workflows/rust-skipped.yml | 4 +- .github/workflows/rust.yml | 4 +- .github/workflows/static-analysis.yml | 2 +- rust/Cargo.lock | 2422 +++++++++++------ rust/Cargo.toml | 205 +- .../agents/relayer/src/msg/gas_payment/mod.rs | 1 + .../metadata/multisig/merkle_root_multisig.rs | 9 +- .../metadata/multisig/message_id_multisig.rs | 10 +- rust/agents/relayer/src/relayer.rs | 16 +- rust/agents/scraper/src/chain_scraper/mod.rs | 24 +- rust/agents/validator/Cargo.toml | 2 + rust/agents/validator/src/settings.rs | 3 +- rust/agents/validator/src/validator.rs | 64 +- rust/chains/hyperlane-cosmos/Cargo.toml | 35 + .../hyperlane-cosmos/src/aggregation_ism.rs | 77 + rust/chains/hyperlane-cosmos/src/error.rs | 43 + .../hyperlane-cosmos/src/interchain_gas.rs | 302 ++ .../src/interchain_security_module.rs | 91 + rust/chains/hyperlane-cosmos/src/lib.rs | 29 + .../hyperlane-cosmos/src/libs/address.rs | 148 + rust/chains/hyperlane-cosmos/src/libs/mod.rs | 2 + rust/chains/hyperlane-cosmos/src/mailbox.rs | 417 +++ .../hyperlane-cosmos/src/merkle_tree_hook.rs | 370 +++ .../hyperlane-cosmos/src/multisig_ism.rs | 85 + .../src/payloads/aggregate_ism.rs | 30 + .../hyperlane-cosmos/src/payloads/general.rs | 34 + .../src/payloads/ism_routes.rs | 43 + .../hyperlane-cosmos/src/payloads/mailbox.rs | 81 + .../src/payloads/merkle_tree_hook.rs | 46 + .../hyperlane-cosmos/src/payloads/mod.rs | 7 + .../src/payloads/multisig_ism.rs | 17 + .../src/payloads/validator_announce.rs | 42 + .../hyperlane-cosmos/src/providers/grpc.rs | 372 +++ .../hyperlane-cosmos/src/providers/mod.rs | 50 + .../hyperlane-cosmos/src/providers/rpc.rs | 236 ++ .../hyperlane-cosmos/src/routing_ism.rs | 83 + rust/chains/hyperlane-cosmos/src/signers.rs | 47 + .../hyperlane-cosmos/src/trait_builder.rs | 78 + rust/chains/hyperlane-cosmos/src/types.rs | 34 + rust/chains/hyperlane-cosmos/src/utils.rs | 46 + .../src/validator_announce.rs | 116 + rust/chains/hyperlane-ethereum/Cargo.toml | 2 +- .../src/merkle_tree_hook.rs | 14 +- .../hyperlane-ethereum/tests/signer_output.rs | 2 +- rust/chains/hyperlane-fuel/src/lib.rs | 11 +- .../hyperlane-fuel/src/validator_announce.rs | 19 +- rust/config/mainnet3_config.json | 94 +- .../test_deployer-account.json | 13 - .../test_deployer-keypair.json | 1 - rust/config/test_sealevel_config.json | 48 - rust/config/testnet4_config.json | 26 + rust/hyperlane-base/Cargo.toml | 3 + .../src/contract_sync/cursor.rs | 159 +- rust/hyperlane-base/src/contract_sync/mod.rs | 23 +- .../src/db/rocks/hyperlane_db.rs | 60 +- rust/hyperlane-base/src/settings/base.rs | 13 +- rust/hyperlane-base/src/settings/chains.rs | 154 +- .../src/settings/loader/arguments.rs | 2 +- rust/hyperlane-base/src/settings/mod.rs | 1 + .../src/settings/parser/connection_parser.rs | 131 + .../hyperlane-base/src/settings/parser/mod.rs | 65 +- rust/hyperlane-base/src/settings/signers.rs | 94 +- rust/hyperlane-base/src/types/multisig.rs | 4 +- rust/hyperlane-core/Cargo.toml | 22 +- .../src/accumulator/incremental.rs | 6 +- rust/hyperlane-core/src/chain.rs | 14 +- rust/hyperlane-core/src/error.rs | 45 + rust/hyperlane-core/src/traits/cursor.rs | 11 +- rust/hyperlane-core/src/traits/db.rs | 27 +- rust/hyperlane-core/src/traits/signing.rs | 6 +- rust/hyperlane-core/src/types/merkle_tree.rs | 10 +- rust/hyperlane-core/src/types/message.rs | 10 +- rust/hyperlane-core/src/types/mod.rs | 2 +- .../src/types/primitive_types.rs | 3 +- rust/hyperlane-core/src/utils.rs | 15 +- rust/utils/run-locally/Cargo.toml | 11 +- rust/utils/run-locally/src/cosmos/cli.rs | 434 +++ rust/utils/run-locally/src/cosmos/crypto.rs | 47 + rust/utils/run-locally/src/cosmos/deploy.rs | 199 ++ rust/utils/run-locally/src/cosmos/link.rs | 270 ++ rust/utils/run-locally/src/cosmos/mod.rs | 587 ++++ rust/utils/run-locally/src/cosmos/rpc.rs | 42 + rust/utils/run-locally/src/cosmos/source.rs | 157 ++ rust/utils/run-locally/src/cosmos/types.rs | 173 ++ rust/utils/run-locally/src/cosmos/utils.rs | 60 + rust/utils/run-locally/src/main.rs | 3 +- 93 files changed, 7536 insertions(+), 1329 deletions(-) create mode 100644 rust/chains/hyperlane-cosmos/Cargo.toml create mode 100644 rust/chains/hyperlane-cosmos/src/aggregation_ism.rs create mode 100644 rust/chains/hyperlane-cosmos/src/error.rs create mode 100644 rust/chains/hyperlane-cosmos/src/interchain_gas.rs create mode 100644 rust/chains/hyperlane-cosmos/src/interchain_security_module.rs create mode 100644 rust/chains/hyperlane-cosmos/src/lib.rs create mode 100644 rust/chains/hyperlane-cosmos/src/libs/address.rs create mode 100644 rust/chains/hyperlane-cosmos/src/libs/mod.rs create mode 100644 rust/chains/hyperlane-cosmos/src/mailbox.rs create mode 100644 rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs create mode 100644 rust/chains/hyperlane-cosmos/src/multisig_ism.rs create mode 100644 rust/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs create mode 100644 rust/chains/hyperlane-cosmos/src/payloads/general.rs create mode 100644 rust/chains/hyperlane-cosmos/src/payloads/ism_routes.rs create mode 100644 rust/chains/hyperlane-cosmos/src/payloads/mailbox.rs create mode 100644 rust/chains/hyperlane-cosmos/src/payloads/merkle_tree_hook.rs create mode 100644 rust/chains/hyperlane-cosmos/src/payloads/mod.rs create mode 100644 rust/chains/hyperlane-cosmos/src/payloads/multisig_ism.rs create mode 100644 rust/chains/hyperlane-cosmos/src/payloads/validator_announce.rs create mode 100644 rust/chains/hyperlane-cosmos/src/providers/grpc.rs create mode 100644 rust/chains/hyperlane-cosmos/src/providers/mod.rs create mode 100644 rust/chains/hyperlane-cosmos/src/providers/rpc.rs create mode 100644 rust/chains/hyperlane-cosmos/src/routing_ism.rs create mode 100644 rust/chains/hyperlane-cosmos/src/signers.rs create mode 100644 rust/chains/hyperlane-cosmos/src/trait_builder.rs create mode 100644 rust/chains/hyperlane-cosmos/src/types.rs create mode 100644 rust/chains/hyperlane-cosmos/src/utils.rs create mode 100644 rust/chains/hyperlane-cosmos/src/validator_announce.rs delete mode 100644 rust/config/test-sealevel-keys/test_deployer-account.json delete mode 100644 rust/config/test-sealevel-keys/test_deployer-keypair.json delete mode 100644 rust/config/test_sealevel_config.json create mode 100644 rust/hyperlane-base/src/settings/parser/connection_parser.rs create mode 100644 rust/utils/run-locally/src/cosmos/cli.rs create mode 100644 rust/utils/run-locally/src/cosmos/crypto.rs create mode 100644 rust/utils/run-locally/src/cosmos/deploy.rs create mode 100644 rust/utils/run-locally/src/cosmos/link.rs create mode 100644 rust/utils/run-locally/src/cosmos/mod.rs create mode 100644 rust/utils/run-locally/src/cosmos/rpc.rs create mode 100644 rust/utils/run-locally/src/cosmos/source.rs create mode 100644 rust/utils/run-locally/src/cosmos/types.rs create mode 100644 rust/utils/run-locally/src/cosmos/utils.rs diff --git a/.github/workflows/agent-release-artifacts.yml b/.github/workflows/agent-release-artifacts.yml index 76b782f5e1..79c421f22a 100644 --- a/.github/workflows/agent-release-artifacts.yml +++ b/.github/workflows/agent-release-artifacts.yml @@ -16,7 +16,7 @@ env: jobs: prepare: - runs-on: ubuntu-latest + runs-on: larger-runner outputs: tag_date: ${{ steps.taggen.outputs.TAG_DATE }} tag_sha: ${{ steps.taggen.outputs.TAG_SHA }} @@ -33,7 +33,7 @@ jobs: matrix: include: - TARGET: x86_64-unknown-linux-gnu - OS: ubuntu-latest + OS: larger-runner - TARGET: x86_64-apple-darwin OS: macos-latest - TARGET: aarch64-apple-darwin @@ -45,7 +45,7 @@ jobs: - name: checkout uses: actions/checkout@v3 - name: ubuntu setup - if: ${{ matrix.OS == 'ubuntu-latest' }} + if: ${{ matrix.OS == 'larger-runner' }} run: | sudo apt-get update -qq sudo apt-get install -qq crossbuild-essential-arm64 crossbuild-essential-armhf @@ -70,7 +70,7 @@ jobs: run: cargo build --release --target ${{ matrix.TARGET }} --bin relayer --bin scraper --bin validator working-directory: ./rust - name: make executable - if: ${{ matrix.OS == 'ubuntu-latest' || matrix.OS == 'macos-latest' }} + if: ${{ matrix.OS == 'larger-runner' || matrix.OS == 'macos-latest' }} run: chmod ug+x,-w relayer scraper validator working-directory: rust/target/${{ matrix.TARGET }}/release - name: upload binaries diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 7091183146..9481b2d145 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -4,7 +4,6 @@ on: push: branches: [main] pull_request: - branches: '*' workflow_dispatch: concurrency: @@ -21,7 +20,7 @@ defaults: jobs: e2e: - runs-on: ubuntu-latest + runs-on: larger-runner steps: - uses: actions/setup-node@v3 with: @@ -68,7 +67,9 @@ jobs: key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} - name: build test run: cargo build --release --bin run-locally - - name: run test + - name: run CosmWasm test + run: RUST_BACKTRACE=1 cargo test --package run-locally --bin run-locally -- cosmos::test --nocapture + - name: run test (excluding CosmWasm) run: ./target/release/run-locally env: E2E_CI_MODE: 'true' diff --git a/.github/workflows/mergify.yml.bak b/.github/workflows/mergify.yml.bak index 3cde1aee3c..2adc55518f 100644 --- a/.github/workflows/mergify.yml.bak +++ b/.github/workflows/mergify.yml.bak @@ -24,7 +24,7 @@ on: jobs: automerge: - runs-on: ubuntu-latest + runs-on: larger-runner steps: - name: automerge @@ -38,7 +38,7 @@ jobs: # in rust.yml complete: - runs-on: ubuntu-latest + runs-on: larger-runner needs: [build, test, lint] steps: @@ -52,7 +52,7 @@ jobs: # in solidity.yml complete: - runs-on: ubuntu-latest + runs-on: larger-runner needs: [install, lint, test] steps: diff --git a/.github/workflows/monorepo-docker.yml b/.github/workflows/monorepo-docker.yml index 9952c3507e..af71889120 100644 --- a/.github/workflows/monorepo-docker.yml +++ b/.github/workflows/monorepo-docker.yml @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true jobs: check-env: - runs-on: ubuntu-latest + runs-on: larger-runner # assign output from step to job output outputs: gcloud-service-key: ${{ steps.gcloud-service-key.outputs.defined }} @@ -27,7 +27,7 @@ jobs: run: echo "::set-output name=defined::true" build-and-push-to-gcr: - runs-on: ubuntu-latest + runs-on: larger-runner # uses check-env to determine if secrets.GCLOUD_SERVICE_KEY is defined needs: [check-env] diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index d2abf4e33d..d39b37c0db 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -14,7 +14,7 @@ env: jobs: yarn-install: - runs-on: ubuntu-latest + runs-on: larger-runner steps: - uses: actions/checkout@v3 with: @@ -39,7 +39,7 @@ jobs: fi yarn-build: - runs-on: ubuntu-latest + runs-on: larger-runner needs: [yarn-install] steps: - uses: actions/checkout@v3 @@ -69,7 +69,7 @@ jobs: run: yarn build lint-prettier: - runs-on: ubuntu-latest + runs-on: larger-runner needs: [yarn-install] steps: - uses: actions/checkout@v3 @@ -97,7 +97,7 @@ jobs: fi test: - runs-on: ubuntu-latest + runs-on: larger-runner needs: [yarn-build] steps: - uses: actions/checkout@v3 @@ -117,7 +117,7 @@ jobs: run: yarn test test-cli: - runs-on: ubuntu-latest + runs-on: larger-runner needs: [yarn-build] steps: - uses: actions/checkout@v3 @@ -136,7 +136,7 @@ jobs: run: ./typescript/cli/ci-test.sh test-env: - runs-on: ubuntu-latest + runs-on: larger-runner needs: [yarn-build] strategy: matrix: @@ -157,7 +157,7 @@ jobs: run: cd typescript/infra && ./fork.sh ${{ matrix.environment }} ${{ matrix.module }} coverage: - runs-on: ubuntu-latest + runs-on: larger-runner needs: [yarn-build] steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 84c0164c0a..25e82feead 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: contents: write pull-requests: write name: Release - runs-on: ubuntu-latest + runs-on: larger-runner steps: - name: Checkout Repo uses: actions/checkout@v3 diff --git a/.github/workflows/rust-docker.yml b/.github/workflows/rust-docker.yml index 7c75426f97..4c64a7cb1c 100644 --- a/.github/workflows/rust-docker.yml +++ b/.github/workflows/rust-docker.yml @@ -12,7 +12,7 @@ concurrency: cancel-in-progress: true jobs: check-env: - runs-on: ubuntu-latest + runs-on: larger-runner # assign output from step to job output outputs: gcloud-service-key: ${{ steps.gcloud-service-key.outputs.defined }} @@ -26,7 +26,7 @@ jobs: run: echo "::set-output name=defined::true" build-and-push-to-gcr: - runs-on: ubuntu-latest + runs-on: larger-runner # uses check-env to determine if secrets.GCLOUD_SERVICE_KEY is defined needs: [check-env] diff --git a/.github/workflows/rust-skipped.yml b/.github/workflows/rust-skipped.yml index bd2669ac17..9a8d0e07ed 100644 --- a/.github/workflows/rust-skipped.yml +++ b/.github/workflows/rust-skipped.yml @@ -12,13 +12,13 @@ env: jobs: test-rs: - runs-on: ubuntu-latest + runs-on: larger-runner steps: - run: 'echo "No test required" ' lint-rs: - runs-on: ubuntu-latest + runs-on: larger-runner steps: - run: 'echo "No lint required" ' diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0546f86913..ebb5623de5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -25,7 +25,7 @@ defaults: jobs: test-rs: - runs-on: ubuntu-latest + runs-on: larger-runner steps: - uses: actions/checkout@v3 @@ -51,7 +51,7 @@ jobs: run: cargo test lint-rs: - runs-on: ubuntu-latest + runs-on: larger-runner steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 5dcf53bad9..9154cd520e 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -9,7 +9,7 @@ on: jobs: slither: - runs-on: ubuntu-latest + runs-on: larger-runner steps: - uses: actions/checkout@v3 diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 0754638d8b..8d1b909db0 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -104,32 +104,32 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.11", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", - "getrandom 0.2.10", "once_cell", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -279,7 +279,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", "synstructure", @@ -291,7 +291,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -304,9 +304,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb42b2197bf15ccb092b62c74515dbd8b86d0effd934795f6687c93b6e679a2c" +checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5" dependencies = [ "brotli", "flate2", @@ -342,20 +342,20 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -371,9 +371,9 @@ dependencies = [ [[package]] name = "atoi" -version = "2.0.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" dependencies = [ "num-traits", ] @@ -405,7 +405,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -417,7 +417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -428,6 +428,51 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -451,12 +496,31 @@ dependencies = [ "derive-new", ] +[[package]] +name = "bae" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b8de67cc41132507eeece2584804efcb15f85ba516e34c944b7667f480397a" +dependencies = [ + "heck 0.3.3", + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "base16ct" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base58" version = "0.1.0" @@ -487,9 +551,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64ct" @@ -542,12 +606,12 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "regex", "rustc-hash", "shlex", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -558,12 +622,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" -dependencies = [ - "serde", -] +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bitmaps" @@ -665,6 +726,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +[[package]] +name = "bnum" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128a44527fc0d6abf05f9eda748b9027536e12dff93f5acc8449f51583309350" + [[package]] name = "borrown" version = "0.1.0" @@ -683,12 +750,12 @@ dependencies = [ [[package]] name = "borsh" -version = "0.10.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +checksum = "bf617fabf5cdbdc92f774bfe5062d870f228b80056d41180797abf48bed4056e" dependencies = [ - "borsh-derive 0.10.3", - "hashbrown 0.13.2", + "borsh-derive 1.2.0", + "cfg_aliases", ] [[package]] @@ -697,24 +764,25 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" dependencies = [ - "borsh-derive-internal 0.9.3", - "borsh-schema-derive-internal 0.9.3", + "borsh-derive-internal", + "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "syn 1.0.109", ] [[package]] name = "borsh-derive" -version = "0.10.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +checksum = "f404657a7ea7b5249e36808dff544bc88a28f26e0ac40009f674b7a009d14be3" dependencies = [ - "borsh-derive-internal 0.10.3", - "borsh-schema-derive-internal 0.10.3", - "proc-macro-crate 0.1.5", - "proc-macro2 1.0.67", - "syn 1.0.109", + "once_cell", + "proc-macro-crate 2.0.0", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.39", + "syn_derive", ] [[package]] @@ -723,18 +791,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "borsh-derive-internal" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" -dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -745,27 +802,16 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "borsh-schema-derive-internal" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" -dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] [[package]] name = "brotli" -version = "3.3.4" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -774,9 +820,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.4" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -842,7 +888,7 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -862,16 +908,16 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -924,9 +970,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" +checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff" dependencies = [ "serde", ] @@ -970,6 +1016,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "chrono" version = "0.4.31" @@ -1047,6 +1099,7 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags 1.3.2", + "clap_derive 3.2.25", "clap_lex 0.2.4", "indexmap 1.9.3", "once_cell", @@ -1057,36 +1110,49 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.6" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" dependencies = [ "clap_builder", - "clap_derive", + "clap_derive 4.4.7", ] [[package]] name = "clap_builder" -version = "4.4.6" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" dependencies = [ "anstream", "anstyle", - "clap_lex 0.5.1", + "clap_lex 0.6.0", "strsim 0.10.0", ] [[package]] name = "clap_derive" -version = "4.4.2" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -1100,9 +1166,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "cobs" @@ -1120,9 +1186,9 @@ dependencies = [ "bs58 0.4.0", "coins-core", "digest 0.10.7", - "getrandom 0.2.10", + "getrandom 0.2.11", "hmac 0.12.1", - "k256", + "k256 0.11.6", "lazy_static", "serde", "sha2 0.10.8", @@ -1137,7 +1203,7 @@ checksum = "2a11892bcac83b4c6e95ab84b5b06c76d9d70ad73548dd07418269c5c7977171" dependencies = [ "bitvec 0.17.4", "coins-bip32", - "getrandom 0.2.10", + "getrandom 0.2.11", "hex 0.4.3", "hmac 0.12.1", "pbkdf2 0.11.0", @@ -1184,9 +1250,9 @@ dependencies = [ [[package]] name = "color-spantrace" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" dependencies = [ "once_cell", "owo-colors", @@ -1343,37 +1409,134 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] -name = "counter" -version = "0.5.7" +name = "cosmos-sdk-proto" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d458e66999348f56fd3ffcfbb7f7951542075ca8359687c703de6500c1ddccd" +checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" dependencies = [ - "num-traits", + "prost", + "prost-types", + "tendermint-proto 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tonic", ] [[package]] -name = "cpufeatures" -version = "0.2.9" +name = "cosmrs" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "af13955d6f356272e6def9ff5e2450a7650df536d8934f47052a20c76513d2f6" dependencies = [ - "libc", + "cosmos-sdk-proto", + "ecdsa 0.16.9", + "eyre", + "getrandom 0.2.11", + "k256 0.13.2", + "rand_core 0.6.4", + "serde", + "serde_json", + "subtle-encoding", + "tendermint", + "tendermint-rpc", + "thiserror", + "tokio", ] [[package]] -name = "crc" -version = "3.0.1" +name = "cosmwasm-crypto" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +checksum = "d8bb3c77c3b7ce472056968c745eb501c440fbc07be5004eba02782c35bfbbe3" dependencies = [ - "crc-catalog", + "digest 0.10.7", + "ecdsa 0.16.9", + "ed25519-zebra", + "k256 0.13.2", + "rand_core 0.6.4", + "thiserror", ] [[package]] -name = "crc-catalog" -version = "2.2.0" +name = "cosmwasm-derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea73e9162e6efde00018d55ed0061e93a108b5d6ec4548b4f8ce3c706249687" +dependencies = [ + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-schema" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df41ea55f2946b6b43579659eec048cc2f66e8c8e2e3652fc5e5e476f673856" +dependencies = [ + "cosmwasm-schema-derive", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43609e92ce1b9368aa951b334dd354a2d0dd4d484931a5f83ae10e12a26c8ba9" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-std" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04d6864742e3a7662d024b51a94ea81c9af21db6faea2f9a6d2232bb97c6e53e" +dependencies = [ + "base64 0.21.5", + "bech32 0.9.1", + "bnum", + "cosmwasm-crypto", + "cosmwasm-derive", + "derivative", + "forward_ref", + "hex 0.4.3", + "schemars", + "serde", + "serde-json-wasm", + "sha2 0.10.8", + "static_assertions", + "thiserror", +] + +[[package]] +name = "cosmwasm-storage" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2b4ae72a03e8f56c85df59d172d51d2d7dc9cec6e2bc811e3fb60c588032a4" +dependencies = [ + "cosmwasm-std", + "serde", +] + +[[package]] +name = "counter" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d458e66999348f56fd3ffcfbb7f7951542075ca8359687c703de6500c1ddccd" +dependencies = [ + "num-traits", +] + +[[package]] +name = "cpufeatures" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] [[package]] name = "crc32fast" @@ -1473,6 +1636,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1554,6 +1729,90 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", +] + +[[package]] +name = "cw-storage-plus" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + +[[package]] +name = "cw-utils" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9f351a4e4d81ef7c890e44d903f8c0bdcdc00f094fd3a181eaf70c0eec7a3a" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9431d14f64f49e41c6ef5561ed11a5391c417d0cb16455dea8cdcb9037a8d197" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw20" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786e9da5e937f473cecd2463e81384c1af65d0f6398bbd851be7655487c55492" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "schemars", + "serde", +] + +[[package]] +name = "cw20-base" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09558f87fd3d5e4a479761051b3f98ee2fa723d9e484b5679b6058ad0eadf8f1" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "cw2", + "cw20", + "schemars", + "semver", + "serde", + "thiserror", +] + [[package]] name = "cynic" version = "2.2.8" @@ -1578,7 +1837,7 @@ dependencies = [ "darling 0.13.4", "graphql-parser", "once_cell", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "strsim 0.10.0", "syn 1.0.109", @@ -1622,7 +1881,7 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "strsim 0.10.0", "syn 1.0.109", @@ -1636,7 +1895,7 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "strsim 0.10.0", "syn 1.0.109", @@ -1707,7 +1966,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid 0.9.5", - "pem-rfc7468", "zeroize", ] @@ -1727,10 +1985,11 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ + "powerfmt", "serde", ] @@ -1746,7 +2005,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -1757,7 +2016,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -1778,7 +2037,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ "darling 0.14.4", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -1800,7 +2059,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case 0.4.0", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "rustc_version", "syn 1.0.109", @@ -1864,15 +2123,35 @@ dependencies = [ ] [[package]] -name = "dirs-next" -version = "2.0.0" +name = "dirs" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ "cfg-if", "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1890,9 +2169,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -1942,6 +2221,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +[[package]] +name = "dyn-clone" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" + [[package]] name = "eager" version = "0.1.0" @@ -1956,15 +2241,29 @@ checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ "der 0.6.1", "elliptic-curve 0.12.3", - "rfc6979", + "rfc6979 0.3.1", "signature 1.6.4", ] +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der 0.7.8", + "digest 0.10.7", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", + "signature 2.2.0", + "spki 0.7.2", +] + [[package]] name = "ecdsa-signature" version = "0.1.0" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.11", "hyperlane-core", "solana-program", "thiserror", @@ -1979,13 +2278,36 @@ dependencies = [ "signature 1.6.4", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.2.0", +] + +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex 0.4.3", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", +] + [[package]] name = "ed25519-dalek" version = "1.0.1" source = "git+https://github.com/Eclipse-Laboratories-Inc/ed25519-dalek?branch=main#7529d65506147b6cb24ca6d8f4fc062cac33b395" dependencies = [ "curve25519-dalek", - "ed25519", + "ed25519 1.5.3", "rand 0.7.3", "serde", "sha2 0.9.9", @@ -2004,6 +2326,21 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek", + "hashbrown 0.12.3", + "hex 0.4.3", + "rand_core 0.6.4", + "serde", + "sha2 0.9.9", + "zeroize", +] + [[package]] name = "educe" version = "0.4.23" @@ -2011,7 +2348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -2021,9 +2358,6 @@ name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" -dependencies = [ - "serde", -] [[package]] name = "elliptic-curve" @@ -2031,7 +2365,7 @@ version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" dependencies = [ - "base16ct", + "base16ct 0.1.1", "crypto-bigint 0.3.2", "der 0.5.1", "generic-array 0.14.7", @@ -2046,16 +2380,35 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", + "base16ct 0.1.1", "crypto-bigint 0.4.9", "der 0.6.1", "digest 0.10.7", - "ff", + "ff 0.12.1", "generic-array 0.14.7", - "group", + "group 0.12.1", "pkcs8 0.9.0", "rand_core 0.6.4", - "sec1", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.5", + "digest 0.10.7", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.3", "subtle", "zeroize", ] @@ -2096,22 +2449,22 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8958699f9359f0b04e691a13850d48b7de329138023876d07cbd024c2c820598" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] [[package]] name = "enum-ordinalize" -version = "3.1.13" +version = "3.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4f76552f53cefc9a7f64987c3701b99d982f7690606fd67de1d09712fbf52f1" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" dependencies = [ "num-bigint 0.4.4", "num-traits", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -2121,9 +2474,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" dependencies = [ "once_cell", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -2141,9 +2494,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" dependencies = [ "humantime", "is-terminal", @@ -2160,33 +2513,11 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" dependencies = [ - "cc", "libc", -] - -[[package]] -name = "etcetera" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" -dependencies = [ - "cfg-if", - "home", "windows-sys 0.48.0", ] @@ -2335,9 +2666,9 @@ dependencies = [ "dunce", "ethers-core", "eyre", - "getrandom 0.2.10", + "getrandom 0.2.11", "hex 0.4.3", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "regex", "reqwest", @@ -2357,7 +2688,7 @@ dependencies = [ "ethers-contract-abigen", "ethers-core", "hex 0.4.3", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "serde_json", "syn 1.0.109", @@ -2377,10 +2708,10 @@ dependencies = [ "ethabi", "generic-array 0.14.7", "hex 0.4.3", - "k256", + "k256 0.11.6", "once_cell", "open-fastrlp", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "rand 0.8.5", "rlp", "rlp-derive", @@ -2399,7 +2730,7 @@ version = "1.0.2" source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2023-06-01#6c26645cc1707a3d9c987a603a513fb38a5edb3f" dependencies = [ "ethers-core", - "getrandom 0.2.10", + "getrandom 0.2.11", "reqwest", "semver", "serde", @@ -2468,7 +2799,7 @@ dependencies = [ "futures-core", "futures-timer", "futures-util", - "getrandom 0.2.10", + "getrandom 0.2.11", "hashers", "hex 0.4.3", "http", @@ -2535,9 +2866,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +checksum = "80f656be11ddf91bd709454d15d5bd896fbaf4cc3314e69349e4d1569f5b46cd" dependencies = [ "indenter", "once_cell", @@ -2571,6 +2902,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "filetime" version = "0.2.22" @@ -2614,32 +2955,31 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] -name = "float-cmp" -version = "0.9.0" +name = "flex-error" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" dependencies = [ - "num-traits", + "eyre", + "paste", ] [[package]] -name = "flume" -version = "0.11.0" +name = "float-cmp" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ - "futures-core", - "futures-sink", - "spin 0.9.8", + "num-traits", ] [[package]] @@ -2672,6 +3012,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "forward_ref" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" + [[package]] name = "fragile" version = "2.0.0" @@ -2781,7 +3127,7 @@ dependencies = [ "coins-bip32", "coins-bip39", "fuel-types", - "getrandom 0.2.10", + "getrandom 0.2.11", "lazy_static", "rand 0.8.5", "secp256k1", @@ -2887,7 +3233,7 @@ dependencies = [ "fuel-abi-types", "itertools 0.10.5", "lazy_static", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "regex", "serde_json", @@ -2920,7 +3266,7 @@ dependencies = [ "fuels-code-gen", "itertools 0.10.5", "lazy_static", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "rand 0.8.5", "regex", @@ -2945,7 +3291,7 @@ dependencies = [ "futures", "hex 0.4.3", "itertools 0.10.5", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "rand 0.8.5", "regex", "serde", @@ -3029,7 +3375,7 @@ dependencies = [ "hex 0.4.3", "itertools 0.10.5", "lazy_static", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "regex", "serde", "serde_json", @@ -3046,9 +3392,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -3061,9 +3407,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -3071,15 +3417,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -3088,20 +3434,20 @@ dependencies = [ [[package]] name = "futures-intrusive" -version = "0.5.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" dependencies = [ "futures-core", "lock_api", - "parking_lot 0.12.1", + "parking_lot 0.11.2", ] [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-locks" @@ -3115,26 +3461,26 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-timer" @@ -3144,9 +3490,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -3187,6 +3533,7 @@ dependencies = [ "serde", "typenum", "version_check", + "zeroize", ] [[package]] @@ -3214,9 +3561,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "js-sys", @@ -3264,16 +3611,27 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] [[package]] name = "h2" -version = "0.3.21" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -3281,10 +3639,10 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap 2.1.0", "slab", "tokio", - "tokio-util 0.7.9", + "tokio-util 0.7.10", "tracing", ] @@ -3303,7 +3661,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.7", ] [[package]] @@ -3312,7 +3670,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.7", ] [[package]] @@ -3321,16 +3679,16 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", ] [[package]] name = "hashbrown" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", "allocator-api2", ] @@ -3349,7 +3707,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.1", + "hashbrown 0.14.2", ] [[package]] @@ -3358,7 +3716,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "bytes", "headers-core", "http", @@ -3503,11 +3861,33 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "hpl-interface" +version = "0.0.6-rc3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9124bd88d88aba044ae790007bcbf25493b93424156e8759523c9c5d02229b0c" +dependencies = [ + "bech32 0.9.1", + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-storage-plus", + "cw2", + "cw20", + "cw20-base", + "ripemd", + "schemars", + "serde", + "sha2 0.10.8", + "sha3 0.10.8", + "thiserror", +] + [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -3560,13 +3940,32 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "tower-service", "tracing", "want", ] +[[package]] +name = "hyper-proxy" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" +dependencies = [ + "bytes", + "futures", + "headers", + "http", + "hyper", + "hyper-rustls 0.22.1", + "rustls-native-certs 0.5.0", + "tokio", + "tokio-rustls 0.22.0", + "tower-service", + "webpki 0.21.4", +] + [[package]] name = "hyper-rustls" version = "0.22.1" @@ -3587,14 +3986,14 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", "hyper", - "rustls 0.21.7", + "rustls 0.21.9", "tokio", "tokio-rustls 0.24.1", ] @@ -3643,6 +4042,7 @@ dependencies = [ "fuels", "futures-util", "hyperlane-core", + "hyperlane-cosmos", "hyperlane-ethereum", "hyperlane-fuel", "hyperlane-sealevel", @@ -3657,6 +4057,7 @@ dependencies = [ "rusoto_sts", "serde", "serde_json", + "solana-sdk", "static_assertions", "tempfile", "thiserror", @@ -3665,6 +4066,7 @@ dependencies = [ "tracing-error", "tracing-futures", "tracing-subscriber", + "url", "walkdir", "warp", ] @@ -3687,11 +4089,11 @@ dependencies = [ "ethers-providers", "eyre", "fixed-hash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "getrandom 0.2.10", + "getrandom 0.2.11", "hex 0.4.3", "itertools 0.11.0", "num 0.4.1", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "primitive-types", "serde", @@ -3705,6 +4107,36 @@ dependencies = [ "uint", ] +[[package]] +name = "hyperlane-cosmos" +version = "0.1.0" +dependencies = [ + "async-trait", + "base64 0.21.5", + "bech32 0.9.1", + "cosmrs", + "derive-new", + "hex 0.4.3", + "hpl-interface", + "hyper", + "hyper-tls", + "hyperlane-core", + "once_cell", + "ripemd", + "serde", + "serde_json", + "sha2 0.10.8", + "sha256", + "tendermint", + "tendermint-rpc", + "thiserror", + "tokio", + "tonic", + "tracing", + "tracing-futures", + "url", +] + [[package]] name = "hyperlane-ethereum" version = "0.1.0" @@ -3755,7 +4187,7 @@ dependencies = [ "account-utils", "anyhow", "async-trait", - "base64 0.21.4", + "base64 0.21.5", "borsh 0.9.3", "derive-new", "hyperlane-core", @@ -3788,7 +4220,7 @@ dependencies = [ "bincode", "borsh 0.9.3", "bs58 0.5.0", - "clap 4.4.6", + "clap 4.4.8", "hex 0.4.3", "hyperlane-core", "hyperlane-sealevel-connection-client", @@ -3851,9 +4283,9 @@ dependencies = [ "access-control", "account-utils", "borsh 0.9.3", - "getrandom 0.2.10", + "getrandom 0.2.11", "hyperlane-core", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "serde", "serializable-account-meta", @@ -3892,16 +4324,16 @@ version = "0.1.0" dependencies = [ "access-control", "account-utils", - "base64 0.21.4", + "base64 0.21.5", "blake3", "borsh 0.9.3", - "getrandom 0.2.10", + "getrandom 0.2.11", "hyperlane-core", "hyperlane-sealevel-interchain-security-module-interface", "hyperlane-sealevel-message-recipient-interface", "itertools 0.11.0", "log", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "proc-macro-crate 1.2.1", "serializable-account-meta", @@ -3916,7 +4348,7 @@ version = "0.1.0" dependencies = [ "access-control", "account-utils", - "base64 0.21.4", + "base64 0.21.5", "borsh 0.9.3", "hyperlane-core", "hyperlane-sealevel-interchain-security-module-interface", @@ -3927,7 +4359,7 @@ dependencies = [ "hyperlane-test-utils", "itertools 0.11.0", "log", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "serializable-account-meta", "solana-program", @@ -3942,7 +4374,7 @@ name = "hyperlane-sealevel-message-recipient-interface" version = "0.1.0" dependencies = [ "borsh 0.9.3", - "getrandom 0.2.10", + "getrandom 0.2.11", "hyperlane-core", "solana-program", "spl-type-length-value", @@ -3963,7 +4395,7 @@ dependencies = [ "hyperlane-sealevel-multisig-ism-message-id", "hyperlane-test-utils", "multisig-ism", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "serializable-account-meta", "solana-program", @@ -4018,7 +4450,7 @@ dependencies = [ "hyperlane-sealevel-test-ism", "hyperlane-sealevel-token-lib", "hyperlane-test-utils", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "serializable-account-meta", "solana-program", @@ -4045,7 +4477,7 @@ dependencies = [ "hyperlane-sealevel-test-ism", "hyperlane-sealevel-token-lib", "hyperlane-test-utils", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "serializable-account-meta", "solana-program", @@ -4070,7 +4502,7 @@ dependencies = [ "hyperlane-sealevel-igp", "hyperlane-sealevel-mailbox", "hyperlane-sealevel-message-recipient-interface", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "serializable-account-meta", "solana-program", @@ -4095,7 +4527,7 @@ dependencies = [ "hyperlane-sealevel-test-ism", "hyperlane-sealevel-token-lib", "hyperlane-test-utils", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "serializable-account-meta", "solana-program", @@ -4164,16 +4596,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -4295,7 +4727,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -4308,9 +4740,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "index_list" -version = "0.2.7" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9d968042a4902e08810946fc7cd5851eb75e80301342305af755ca06cb82ce" +checksum = "70891286cb8e844fdfcf1178b47569699f9e20b5ecc4b45a6240a64771444638" [[package]] name = "indexmap" @@ -4324,12 +4756,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.1", + "hashbrown 0.14.2", ] [[package]] @@ -4344,17 +4776,6 @@ dependencies = [ "regex", ] -[[package]] -name = "inherent" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce243b1bfa62ffc028f1cc3b6034ec63d649f3031bc8a4fbbb004e1ac17d1f68" -dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", -] - [[package]] name = "inout" version = "0.1.3" @@ -4378,9 +4799,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" @@ -4419,18 +4840,18 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ "wasm-bindgen", ] @@ -4468,12 +4889,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if", - "ecdsa", + "ecdsa 0.14.8", "elliptic-curve 0.12.3", "sha2 0.10.8", "sha3 0.10.8", ] +[[package]] +name = "k256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" +dependencies = [ + "cfg-if", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "once_cell", + "sha2 0.10.8", + "signature 2.2.0", +] + [[package]] name = "keccak" version = "0.1.4" @@ -4488,9 +4923,6 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin 0.5.2", -] [[package]] name = "lazycell" @@ -4500,9 +4932,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" @@ -4515,10 +4947,15 @@ dependencies = [ ] [[package]] -name = "libm" -version = "0.2.7" +name = "libredox" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] [[package]] name = "librocksdb-sys" @@ -4584,17 +5021,6 @@ dependencies = [ "libsecp256k1-core", ] -[[package]] -name = "libsqlite3-sys" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - [[package]] name = "libz-sys" version = "1.1.12" @@ -4614,15 +5040,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -4700,6 +5126,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "md-5" version = "0.9.1" @@ -4723,9 +5155,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memmap2" @@ -4812,9 +5244,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -4843,7 +5275,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -4864,7 +5296,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -4930,13 +5362,24 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "nix" version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "cfg-if", "libc", ] @@ -5019,25 +5462,8 @@ dependencies = [ ] [[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-complex" -version = "0.2.4" +name = "num-complex" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" dependencies = [ @@ -5061,20 +5487,20 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] [[package]] name = "num-derive" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e" +checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -5125,12 +5551,11 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -5168,7 +5593,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -5180,9 +5605,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -5247,18 +5672,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] [[package]] name = "openssl" -version = "0.10.57" +version = "0.10.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -5273,9 +5698,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -5286,9 +5711,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.93" +version = "0.9.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" dependencies = [ "cc", "libc", @@ -5315,15 +5740,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "ordered-float" -version = "3.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a54938017eacd63036332b4ae5c8a49fc8c0c1d6d629893057e4f13609edd06" -dependencies = [ - "num-traits", -] - [[package]] name = "ordered-multimap" version = "0.4.3" @@ -5336,9 +5752,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.5.1" +version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "ouroboros" @@ -5347,18 +5763,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" dependencies = [ "aliasable", - "ouroboros_macro 0.15.6", -] - -[[package]] -name = "ouroboros" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954" -dependencies = [ - "aliasable", - "ouroboros_macro 0.17.2", - "static_assertions", + "ouroboros_macro", ] [[package]] @@ -5369,24 +5774,11 @@ checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] -[[package]] -name = "ouroboros_macro" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" -dependencies = [ - "heck 0.4.1", - "proc-macro-error", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", -] - [[package]] name = "overload" version = "0.1.1" @@ -5420,7 +5812,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -5443,7 +5835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.8", + "parking_lot_core 0.9.9", ] [[package]] @@ -5462,13 +5854,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] @@ -5548,21 +5940,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] -name = "pem" -version = "1.1.1" +name = "peg" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a" dependencies = [ - "base64 0.13.1", + "peg-macros", + "peg-runtime", ] [[package]] -name = "pem-rfc7468" +name = "peg-macros" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" dependencies = [ - "base64ct", + "peg-runtime", + "proc-macro2 1.0.69", + "quote 1.0.33", +] + +[[package]] +name = "peg-runtime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088" + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", ] [[package]] @@ -5582,9 +5992,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" dependencies = [ "memchr", "thiserror", @@ -5593,9 +6003,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" +checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" dependencies = [ "pest", "pest_generator", @@ -5603,22 +6013,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" +checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "pest_meta" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" +checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" dependencies = [ "once_cell", "pest", @@ -5650,9 +6060,9 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -5667,17 +6077,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der 0.7.8", - "pkcs8 0.10.2", - "spki 0.7.2", -] - [[package]] name = "pkcs8" version = "0.8.0" @@ -5754,6 +6153,12 @@ dependencies = [ "serde", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -5796,7 +6201,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" dependencies = [ - "env_logger 0.10.0", + "env_logger 0.10.1", "log", ] @@ -5806,8 +6211,8 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ - "proc-macro2 1.0.67", - "syn 2.0.37", + "proc-macro2 1.0.69", + "syn 2.0.39", ] [[package]] @@ -5843,6 +6248,15 @@ dependencies = [ "toml", ] +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -5850,7 +6264,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", "version_check", @@ -5862,7 +6276,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "version_check", ] @@ -5878,9 +6292,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -5900,6 +6314,38 @@ dependencies = [ "thiserror", ] +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost", +] + [[package]] name = "protobuf" version = "2.28.0" @@ -5927,7 +6373,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -5967,7 +6413,7 @@ dependencies = [ "thiserror", "tokio", "tracing", - "webpki 0.22.1", + "webpki 0.22.4", ] [[package]] @@ -5979,7 +6425,7 @@ dependencies = [ "bytes", "fxhash", "rand 0.8.5", - "ring", + "ring 0.16.20", "rustls 0.20.9", "rustls-native-certs 0.6.3", "rustls-pemfile 0.2.1", @@ -5987,7 +6433,7 @@ dependencies = [ "thiserror", "tinyvec", "tracing", - "webpki 0.22.1", + "webpki 0.22.4", ] [[package]] @@ -5999,7 +6445,7 @@ dependencies = [ "futures-util", "libc", "quinn-proto", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "tracing", ] @@ -6019,7 +6465,7 @@ version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", ] [[package]] @@ -6093,7 +6539,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.11", ] [[package]] @@ -6141,7 +6587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", - "ring", + "ring 0.16.20", "time", "yasna", ] @@ -6164,27 +6610,36 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ - "getrandom 0.2.10", - "redox_syscall 0.2.16", + "getrandom 0.2.11", + "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.9.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.8", - "regex-syntax 0.7.5", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -6198,13 +6653,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", ] [[package]] @@ -6215,9 +6670,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "relayer" @@ -6238,7 +6693,7 @@ dependencies = [ "hyperlane-ethereum", "hyperlane-test", "itertools 0.11.0", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "prometheus", "regex", @@ -6264,12 +6719,12 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.20" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "async-compression", - "base64 0.21.4", + "base64 0.21.5", "bytes", "cookie", "cookie_store", @@ -6280,7 +6735,7 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls 0.24.1", + "hyper-rustls 0.24.2", "hyper-tls", "ipnet", "js-sys", @@ -6290,15 +6745,16 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.7", - "rustls-pemfile 1.0.3", + "rustls 0.21.9", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls 0.24.1", - "tokio-util 0.7.9", + "tokio-util 0.7.10", "tower-service", "url", "wasm-bindgen", @@ -6319,6 +6775,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + [[package]] name = "ring" version = "0.16.20" @@ -6329,11 +6795,25 @@ dependencies = [ "libc", "once_cell", "spin 0.5.2", - "untrusted", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +dependencies = [ + "cc", + "getrandom 0.2.11", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + [[package]] name = "ripemd" version = "0.1.3" @@ -6357,7 +6837,7 @@ dependencies = [ "rkyv_derive", "seahash", "tinyvec", - "uuid 1.4.1", + "uuid 1.6.1", ] [[package]] @@ -6366,7 +6846,7 @@ version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -6386,7 +6866,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -6424,40 +6904,27 @@ dependencies = [ "winapi", ] -[[package]] -name = "rsa" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8" -dependencies = [ - "byteorder", - "const-oid 0.9.5", - "digest 0.10.7", - "num-bigint-dig", - "num-integer", - "num-iter", - "num-traits", - "pkcs1", - "pkcs8 0.10.2", - "rand_core 0.6.4", - "signature 2.1.0", - "spki 0.7.2", - "subtle", - "zeroize", -] - [[package]] name = "run-locally" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "ctrlc", "eyre", + "hex 0.4.3", + "hpl-interface", "hyperlane-core", + "k256 0.13.2", "macro_rules_attribute", "maplit", - "nix 0.27.1", + "nix 0.26.4", "regex", + "ripemd", + "serde", + "serde_json", + "sha2 0.10.8", "tempfile", + "toml_edit 0.19.15", "ureq", "which", ] @@ -6585,12 +7052,12 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.32.0" +version = "1.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd" +checksum = "06676aec5ccb8fc1da723cc8c0f9a46549f21ebb8753d3915c6c41db1e7f1dc4" dependencies = [ "arrayvec", - "borsh 0.10.3", + "borsh 1.2.0", "bytes", "num-traits", "rand 0.8.5", @@ -6637,11 +7104,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.14" +version = "0.38.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", @@ -6656,7 +7123,7 @@ checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ "base64 0.13.1", "log", - "ring", + "ring 0.16.20", "sct 0.6.1", "webpki 0.21.4", ] @@ -6668,21 +7135,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", - "ring", - "sct 0.7.0", - "webpki 0.22.1", + "ring 0.16.20", + "sct 0.7.1", + "webpki 0.22.4", ] [[package]] name = "rustls" -version = "0.21.7" +version = "0.21.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" dependencies = [ "log", - "ring", + "ring 0.17.5", "rustls-webpki", - "sct 0.7.0", + "sct 0.7.1", ] [[package]] @@ -6704,7 +7171,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile 1.0.3", + "rustls-pemfile 1.0.4", "schannel", "security-framework", ] @@ -6720,21 +7187,21 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", ] [[package]] name = "rustls-webpki" -version = "0.101.6" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] @@ -6778,9 +7245,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" +checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" dependencies = [ "cfg-if", "derive_more", @@ -6790,12 +7257,12 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" +checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -6809,6 +7276,30 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "serde_derive_internals", + "syn 1.0.109", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -6865,9 +7356,9 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -6902,38 +7393,25 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sea-bae" -version = "0.2.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "heck 0.4.1", - "proc-macro-error", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] name = "sea-orm" -version = "0.12.3" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da5b2d70c255bc5cbe1d49f69c3c8eadae0fbbaeb18ee978edbf2f75775cb94d" +checksum = "fade86e8d41fd1a4721f84cb834f4ca2783f973cc30e6212b7fafc134f169214" dependencies = [ "async-stream", "async-trait", @@ -6941,32 +7419,31 @@ dependencies = [ "chrono", "futures", "log", - "ouroboros 0.17.2", + "ouroboros", "rust_decimal", "sea-orm-macros", "sea-query", "sea-query-binder", + "sea-strum", "serde", "serde_json", "sqlx", - "strum 0.25.0", "thiserror", "time", "tracing", "url", - "uuid 1.4.1", + "uuid 1.6.1", ] [[package]] name = "sea-orm-cli" -version = "0.12.3" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bef60732e6016c5643350c87f43a697e8c074e41e4e2a9d961c056cb1310915" +checksum = "efbf34a2caf70c2e3be9bb1e674e9540f6dfd7c8f40f6f05daf3b9740e476005" dependencies = [ "chrono", - "clap 4.4.6", + "clap 3.2.25", "dotenvy", - "glob", "regex", "sea-schema", "tracing", @@ -6976,26 +7453,25 @@ dependencies = [ [[package]] name = "sea-orm-macros" -version = "0.12.3" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c8d455fad40194fb9774fdc4810c0f2700ff0dc0e93bd5ce9d641cc3f5dd75" +checksum = "28936f26d62234ff0be16f80115dbdeb3237fe9c25cf18fbcd1e3b3592360f20" dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.67", + "bae", + "heck 0.3.3", + "proc-macro2 1.0.69", "quote 1.0.33", - "sea-bae", - "syn 2.0.37", - "unicode-ident", + "syn 1.0.109", ] [[package]] name = "sea-orm-migration" -version = "0.12.3" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e53b6ddaf6dbb84e5dfc3fb78634ed0a4d6d64e7479500ab2585db239747031" +checksum = "278d3adfd0832b6ffc17d3cfbc574d3695a5c1b38814e0bc8ac238d33f3d87cf" dependencies = [ "async-trait", - "clap 4.4.6", + "clap 3.2.25", "dotenvy", "futures", "sea-orm", @@ -7007,27 +7483,24 @@ dependencies = [ [[package]] name = "sea-query" -version = "0.30.2" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3e6bba153bb198646c8762c48414942a38db27d142e44735a133cabddcc820" +checksum = "bbab99b8cd878ab7786157b7eb8df96333a6807cc6e45e8888c85b51534b401a" dependencies = [ "bigdecimal", "chrono", - "derivative", - "inherent", - "ordered-float", "rust_decimal", "sea-query-derive", "serde_json", "time", - "uuid 1.4.1", + "uuid 1.6.1", ] [[package]] name = "sea-query-binder" -version = "0.5.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36bbb68df92e820e4d5aeb17b4acd5cc8b5d18b2c36a4dd6f4626aabfa7ab1b9" +checksum = "4cea85029985b40dfbf18318d85fe985c04db7c1b4e5e8e0a0a0cdff5f1e30f9" dependencies = [ "bigdecimal", "chrono", @@ -7036,17 +7509,17 @@ dependencies = [ "serde_json", "sqlx", "time", - "uuid 1.4.1", + "uuid 1.6.1", ] [[package]] name = "sea-query-derive" -version = "0.4.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd78f2e0ee8e537e9195d1049b752e0433e2cac125426bccb7b5c3e508096117" +checksum = "63f62030c60f3a691f5fe251713b4e220b306e50a71e1d6f9cce1f24bb781978" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", "thiserror", @@ -7054,9 +7527,9 @@ dependencies = [ [[package]] name = "sea-schema" -version = "0.14.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cd9561232bd1b82ea748b581f15909d11de0db6563ddcf28c5d908aee8282f1" +checksum = "eeb2940bb5a10bc6cd05b450ce6cd3993e27fddd7eface2becb97fc5af3a040e" dependencies = [ "futures", "sea-query", @@ -7065,13 +7538,35 @@ dependencies = [ [[package]] name = "sea-schema-derive" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f686050f76bffc4f635cda8aea6df5548666b830b52387e8bc7de11056d11e" +checksum = "56821b7076f5096b8f726e2791ad255a99c82498e08ec477a65a96c461ff1927" dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.67", + "heck 0.3.3", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "sea-strum" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391d06a6007842cfe79ac6f7f53911b76dfd69fc9a6769f1cf6569d12ce20e1b" +dependencies = [ + "sea-strum_macros", +] + +[[package]] +name = "sea-strum_macros" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b4397b825df6ccf1e98bcdabef3bbcfc47ff5853983467850eeab878384f21" +dependencies = [ + "heck 0.3.3", + "proc-macro2 1.0.69", "quote 1.0.33", + "rustversion", "syn 1.0.109", ] @@ -7087,7 +7582,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", + "base16ct 0.1.1", "der 0.6.1", "generic-array 0.14.7", "pkcs8 0.9.0", @@ -7095,6 +7590,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.8", + "generic-array 0.14.7", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + [[package]] name = "secp256k1" version = "0.24.3" @@ -7148,9 +7657,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" dependencies = [ "serde", ] @@ -7163,9 +7672,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] @@ -7180,6 +7689,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "serde-json-wasm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +dependencies = [ + "serde", +] + [[package]] name = "serde_bytes" version = "0.11.12" @@ -7191,24 +7709,46 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] -name = "serde_json" -version = "1.0.107" +name = "serde_derive_internals" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ - "itoa", - "ryu", - "serde", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.39", ] [[package]] @@ -7241,7 +7781,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling 0.13.4", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -7324,6 +7864,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha256" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7895c8ae88588ccead14ff438b939b0c569cd619116f14b4d13fdff7b8333386" +dependencies = [ + "async-trait", + "bytes", + "hex 0.4.3", + "sha2 0.10.8", + "tokio", +] + [[package]] name = "sha3" version = "0.9.1" @@ -7348,9 +7901,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -7388,9 +7941,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core 0.6.4", @@ -7423,15 +7976,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -7439,9 +7992,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys 0.48.0", @@ -7703,7 +8256,7 @@ name = "solana-frozen-abi" version = "1.14.13" source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.7", "blake3", "block-buffer 0.9.0", "bs58 0.4.0", @@ -7736,7 +8289,7 @@ name = "solana-frozen-abi-macro" version = "1.14.13" source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "rustc_version", "syn 1.0.109", @@ -7787,7 +8340,7 @@ dependencies = [ "rand 0.7.3", "serde", "serde_derive", - "socket2 0.4.9", + "socket2 0.4.10", "solana-logger", "solana-sdk", "solana-version", @@ -7800,7 +8353,7 @@ name = "solana-perf" version = "1.14.13" source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.7", "bincode", "bv", "caps", @@ -7839,7 +8392,7 @@ dependencies = [ "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.2.10", + "getrandom 0.2.11", "itertools 0.10.5", "js-sys", "lazy_static", @@ -7975,7 +8528,7 @@ dependencies = [ "num-traits", "num_cpus", "once_cell", - "ouroboros 0.15.6", + "ouroboros", "rand 0.7.3", "rayon", "regex", @@ -8062,7 +8615,7 @@ version = "1.14.13" source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" dependencies = [ "bs58 0.4.0", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "rustversion", "syn 1.0.109", @@ -8400,217 +8953,98 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.7.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e50c216e3624ec8e7ecd14c6a6a6370aad6ee5d8cfc3ab30b5162eeeef2ed33" +checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" dependencies = [ "sqlx-core", "sqlx-macros", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", ] [[package]] name = "sqlx-core" -version = "0.7.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d" +checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" dependencies = [ - "ahash 0.8.3", + "ahash 0.7.7", "atoi", + "base64 0.13.1", "bigdecimal", + "bitflags 1.3.2", "byteorder", "bytes", "chrono", - "crc", "crossbeam-queue", + "dirs", "dotenvy", "either", "event-listener", "futures-channel", "futures-core", "futures-intrusive", - "futures-io", "futures-util", "hashlink", "hex 0.4.3", - "indexmap 2.0.1", + "hkdf", + "hmac 0.12.1", + "indexmap 1.9.3", + "itoa", + "libc", "log", + "md-5 0.10.6", "memchr", - "native-tls", + "num-bigint 0.4.4", "once_cell", "paste", "percent-encoding", + "rand 0.8.5", "rust_decimal", "serde", "serde_json", + "sha1", "sha2 0.10.8", "smallvec", "sqlformat", + "sqlx-rt", + "stringprep", "thiserror", "time", - "tokio", "tokio-stream", - "tracing", "url", - "uuid 1.4.1", + "uuid 1.6.1", + "whoami", ] [[package]] name = "sqlx-macros" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a793bb3ba331ec8359c1853bd39eed32cdd7baaf22c35ccf5c92a7e8d1189ec" -dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "sqlx-core", - "sqlx-macros-core", - "syn 1.0.109", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.7.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4ee1e104e00dedb6aa5ffdd1343107b0a4702e862a84320ee7cc74782d96fc" +checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" dependencies = [ "dotenvy", "either", "heck 0.4.1", - "hex 0.4.3", "once_cell", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "serde", "serde_json", - "sha2 0.10.8", "sqlx-core", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", + "sqlx-rt", "syn 1.0.109", - "tempfile", - "tokio", "url", ] [[package]] -name = "sqlx-mysql" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db" -dependencies = [ - "atoi", - "base64 0.21.4", - "bigdecimal", - "bitflags 2.4.0", - "byteorder", - "bytes", - "chrono", - "crc", - "digest 0.10.7", - "dotenvy", - "either", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "generic-array 0.14.7", - "hex 0.4.3", - "hkdf", - "hmac 0.12.1", - "itoa", - "log", - "md-5 0.10.6", - "memchr", - "once_cell", - "percent-encoding", - "rand 0.8.5", - "rsa", - "rust_decimal", - "serde", - "sha1", - "sha2 0.10.8", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "time", - "tracing", - "uuid 1.4.1", - "whoami", -] - -[[package]] -name = "sqlx-postgres" -version = "0.7.2" +name = "sqlx-rt" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624" +checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" dependencies = [ - "atoi", - "base64 0.21.4", - "bigdecimal", - "bitflags 2.4.0", - "byteorder", - "chrono", - "crc", - "dotenvy", - "etcetera", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "hex 0.4.3", - "hkdf", - "hmac 0.12.1", - "home", - "itoa", - "log", - "md-5 0.10.6", - "memchr", - "num-bigint 0.4.4", + "native-tls", "once_cell", - "rand 0.8.5", - "rust_decimal", - "serde", - "serde_json", - "sha1", - "sha2 0.10.8", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "time", - "tracing", - "uuid 1.4.1", - "whoami", -] - -[[package]] -name = "sqlx-sqlite" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59dc83cf45d89c555a577694534fcd1b55c545a816c816ce51f20bbe56a4f3f" -dependencies = [ - "atoi", - "chrono", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "libsqlite3-sys", - "log", - "percent-encoding", - "serde", - "sqlx-core", - "time", - "tracing", - "url", - "uuid 1.4.1", + "tokio", + "tokio-native-tls", ] [[package]] @@ -8669,7 +9103,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" dependencies = [ - "strum_macros 0.25.2", + "strum_macros 0.25.3", ] [[package]] @@ -8679,7 +9113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -8691,7 +9125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "rustversion", "syn 1.0.109", @@ -8699,15 +9133,15 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "rustversion", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -8716,6 +9150,21 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "subtle-encoding" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" +dependencies = [ + "zeroize", +] + +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "symlink" version = "0.1.0" @@ -8739,34 +9188,73 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.37" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "unicode-ident", ] +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.39", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "synstructure" version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", "unicode-xid 0.2.4", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tai64" version = "4.0.0" @@ -8823,29 +9311,141 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "rustix", "windows-sys 0.48.0", ] +[[package]] +name = "tendermint" +version = "0.32.2" +source = "git+https://github.com/hyperlane-xyz/tendermint-rs.git?branch=trevor/0.32.2-fork#feea28d47e73bf7678e1e5cdced0f5ca51b69286" +dependencies = [ + "bytes", + "digest 0.10.7", + "ed25519 2.2.3", + "ed25519-consensus", + "flex-error", + "futures", + "k256 0.13.2", + "num-traits", + "once_cell", + "prost", + "prost-types", + "ripemd", + "serde", + "serde_bytes", + "serde_json", + "serde_repr", + "sha2 0.10.8", + "signature 2.2.0", + "subtle", + "subtle-encoding", + "tendermint-proto 0.32.2 (git+https://github.com/hyperlane-xyz/tendermint-rs.git?branch=trevor/0.32.2-fork)", + "time", + "zeroize", +] + +[[package]] +name = "tendermint-config" +version = "0.32.2" +source = "git+https://github.com/hyperlane-xyz/tendermint-rs.git?branch=trevor/0.32.2-fork#feea28d47e73bf7678e1e5cdced0f5ca51b69286" +dependencies = [ + "flex-error", + "serde", + "serde_json", + "tendermint", + "toml", + "url", +] + +[[package]] +name = "tendermint-proto" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cec054567d16d85e8c3f6a3139963d1a66d9d3051ed545d31562550e9bcc3d" +dependencies = [ + "bytes", + "flex-error", + "num-derive 0.3.3", + "num-traits", + "prost", + "prost-types", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + +[[package]] +name = "tendermint-proto" +version = "0.32.2" +source = "git+https://github.com/hyperlane-xyz/tendermint-rs.git?branch=trevor/0.32.2-fork#feea28d47e73bf7678e1e5cdced0f5ca51b69286" +dependencies = [ + "bytes", + "flex-error", + "num-derive 0.3.3", + "num-traits", + "prost", + "prost-types", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + +[[package]] +name = "tendermint-rpc" +version = "0.32.2" +source = "git+https://github.com/hyperlane-xyz/tendermint-rs.git?branch=trevor/0.32.2-fork#feea28d47e73bf7678e1e5cdced0f5ca51b69286" +dependencies = [ + "async-trait", + "bytes", + "flex-error", + "futures", + "getrandom 0.2.11", + "http", + "hyper", + "hyper-proxy", + "hyper-rustls 0.22.1", + "peg", + "pin-project", + "semver", + "serde", + "serde_bytes", + "serde_json", + "subtle", + "subtle-encoding", + "tendermint", + "tendermint-config", + "tendermint-proto 0.32.2 (git+https://github.com/hyperlane-xyz/tendermint-rs.git?branch=trevor/0.32.2-fork)", + "thiserror", + "time", + "tokio", + "tracing", + "url", + "uuid 0.8.2", + "walkdir", +] + [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] @@ -8873,22 +9473,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -8903,12 +9503,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", + "powerfmt", "serde", "time-core", "time-macros", @@ -8974,9 +9575,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" dependencies = [ "backtrace", "bytes", @@ -8986,7 +9587,7 @@ dependencies = [ "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.4", + "socket2 0.5.5", "tokio-macros", "windows-sys 0.48.0", ] @@ -9003,13 +9604,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -9041,7 +9642,7 @@ checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ "rustls 0.20.9", "tokio", - "webpki 0.22.1", + "webpki 0.22.4", ] [[package]] @@ -9050,7 +9651,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.7", + "rustls 0.21.9", "tokio", ] @@ -9106,7 +9707,7 @@ dependencies = [ "tokio", "tokio-rustls 0.23.4", "tungstenite 0.17.3", - "webpki 0.22.1", + "webpki 0.22.4", "webpki-roots 0.22.6", ] @@ -9139,9 +9740,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -9160,6 +9761,88 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tonic" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +dependencies = [ + "async-trait", + "axum", + "base64 0.21.5", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util 0.7.10", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -9168,11 +9851,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -9181,20 +9863,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -9245,9 +9927,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", @@ -9286,7 +9968,7 @@ dependencies = [ "thiserror", "url", "utf-8", - "webpki 0.22.1", + "webpki 0.22.4", "webpki-roots 0.22.6", ] @@ -9418,13 +10100,19 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "ureq" -version = "2.7.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b11c96ac7ee530603dcdf68ed1557050f374ce55a5a07193ebf8cbc9f8927e9" +checksum = "7830e33f6e25723d41a63f77e434159dad02919f18f55a512b5f16f3b1d77138" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "log", "once_cell", "url", @@ -9469,15 +10157,15 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.11", "serde", ] [[package]] name = "uuid" -version = "1.4.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ "serde", ] @@ -9494,8 +10182,10 @@ dependencies = [ "futures-util", "hyperlane-base", "hyperlane-core", + "hyperlane-cosmos", "hyperlane-ethereum", "hyperlane-test", + "k256 0.13.2", "prometheus", "serde", "serde_json", @@ -9573,7 +10263,7 @@ dependencies = [ "multer", "percent-encoding", "pin-project", - "rustls-pemfile 1.0.3", + "rustls-pemfile 1.0.4", "scoped-tls", "serde", "serde_json", @@ -9581,7 +10271,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-tungstenite 0.20.1", - "tokio-util 0.7.9", + "tokio-util 0.7.10", "tower-service", "tracing", ] @@ -9600,9 +10290,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -9610,24 +10300,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" dependencies = [ "cfg-if", "js-sys", @@ -9637,9 +10327,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote 1.0.33", "wasm-bindgen-macro-support", @@ -9647,22 +10337,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasm-timer" @@ -9681,9 +10371,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" dependencies = [ "js-sys", "wasm-bindgen", @@ -9695,18 +10385,18 @@ version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] name = "webpki" -version = "0.22.1" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring", - "untrusted", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] @@ -9724,7 +10414,7 @@ version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ - "webpki 0.22.1", + "webpki 0.22.4", ] [[package]] @@ -9750,6 +10440,10 @@ name = "whoami" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +dependencies = [ + "wasm-bindgen", + "web-sys", +] [[package]] name = "winapi" @@ -9783,10 +10477,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets 0.48.5", ] @@ -9923,6 +10617,15 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "winnow" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -10012,11 +10715,31 @@ dependencies = [ "time", ] +[[package]] +name = "zerocopy" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] @@ -10027,9 +10750,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.69", "quote 1.0.33", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -10053,11 +10776,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b1400bbf87..c1ee840430 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,43 +1,43 @@ [workspace] members = [ - "agents/relayer", - "agents/scraper", - "agents/validator", - "chains/hyperlane-ethereum", - "chains/hyperlane-fuel", - "chains/hyperlane-sealevel", - "ethers-prometheus", - "hyperlane-base", - "hyperlane-core", - "hyperlane-test", - "sealevel/client", - "sealevel/libraries/access-control", - "sealevel/libraries/account-utils", - "sealevel/libraries/ecdsa-signature", - "sealevel/libraries/hyperlane-sealevel-connection-client", - "sealevel/libraries/hyperlane-sealevel-token", - "sealevel/libraries/interchain-security-module-interface", - "sealevel/libraries/message-recipient-interface", - "sealevel/libraries/multisig-ism", - "sealevel/libraries/serializable-account-meta", - "sealevel/libraries/test-transaction-utils", - "sealevel/libraries/test-utils", - "sealevel/programs/helloworld", - "sealevel/programs/hyperlane-sealevel-token", - "sealevel/programs/hyperlane-sealevel-token-collateral", - "sealevel/programs/hyperlane-sealevel-token-native", - "sealevel/programs/hyperlane-sealevel-igp", - "sealevel/programs/hyperlane-sealevel-igp-test", - "sealevel/programs/ism/multisig-ism-message-id", - "sealevel/programs/ism/test-ism", - "sealevel/programs/mailbox", - "sealevel/programs/mailbox-test", - "sealevel/programs/test-send-receiver", - "sealevel/programs/validator-announce", - "utils/abigen", - "utils/backtrace-oneline", - "utils/hex", - "utils/run-locally", + "agents/relayer", + "agents/scraper", + "agents/validator", + "chains/hyperlane-cosmos", + "chains/hyperlane-ethereum", + "chains/hyperlane-fuel", + "chains/hyperlane-sealevel", + "ethers-prometheus", + "hyperlane-base", + "hyperlane-core", + "hyperlane-test", + "sealevel/client", + "sealevel/libraries/access-control", + "sealevel/libraries/account-utils", + "sealevel/libraries/ecdsa-signature", + "sealevel/libraries/hyperlane-sealevel-connection-client", + "sealevel/libraries/hyperlane-sealevel-token", + "sealevel/libraries/interchain-security-module-interface", + "sealevel/libraries/message-recipient-interface", + "sealevel/libraries/multisig-ism", + "sealevel/libraries/serializable-account-meta", + "sealevel/libraries/test-transaction-utils", + "sealevel/libraries/test-utils", + "sealevel/programs/hyperlane-sealevel-igp", + "sealevel/programs/hyperlane-sealevel-igp-test", + "sealevel/programs/hyperlane-sealevel-token", + "sealevel/programs/hyperlane-sealevel-token-collateral", + "sealevel/programs/hyperlane-sealevel-token-native", + "sealevel/programs/ism/multisig-ism-message-id", + "sealevel/programs/ism/test-ism", + "sealevel/programs/mailbox", + "sealevel/programs/mailbox-test", + "sealevel/programs/test-send-receiver", + "sealevel/programs/validator-announce", + "utils/abigen", + "utils/backtrace-oneline", + "utils/hex", + "utils/run-locally", ] [workspace.package] @@ -56,13 +56,19 @@ auto_impl = "1.0" backtrace = "0.3" base64 = "0.21.2" bincode = "1.3" -borsh = "0.9" # 0.9 is needed for solana 1.14 +borsh = "0.9" bs58 = "0.5.0" bytes = "1" clap = "4" color-eyre = "0.6" config = "0.13.3" convert_case = "0.6" +cosmrs = { version = "0.14", default-features = false, features = [ + "cosmwasm", + "rpc", + "tokio", + "grpc", +] } crunchy = "0.2" ctrlc = "3.2" curve25519-dalek = { version = "~3.2", features = ["serde"] } @@ -77,20 +83,28 @@ fuels = "0.38" fuels-code-gen = "0.38" futures = "0.3" futures-util = "0.3" -generic-array = { version = "1.0", features = ["serde", "more_lengths"] } -getrandom = { version = "0.2", features = ["js"] } # Required for WASM support https://docs.rs/getrandom/latest/getrandom/#webassembly-support -hex = "0.4" +generic-array = { version = "0.14", features = ["serde", "more_lengths"] } +# Required for WASM support https://docs.rs/getrandom/latest/getrandom/#webassembly-support +bech32 = "0.9.1" +elliptic-curve = "0.12.3" +getrandom = { version = "0.2", features = ["js"] } +hex = "0.4.3" +hpl-interface = "=0.0.6-rc3" +hyper = "0.14" +hyper-tls = "0.5.0" itertools = "0.11.0" jsonrpc-core = "18.0" +k256 = { version = "0.13.1", features = ["std", "ecdsa"] } log = "0.4" macro_rules_attribute = "0.2" maplit = "1.0" mockall = "0.11" -nix = { version = "0.27", default-features = false } +nix = { version = "0.26", default-features = false } num = "0.4" num-bigint = "0.4" num-derive = "0.4.0" num-traits = "0.2" +once_cell = "1.18.0" parking_lot = "0.12" paste = "1.0" pretty_env_logger = "0.5.0" @@ -98,16 +112,27 @@ primitive-types = "=0.12.1" prometheus = "0.13" regex = "1.5" reqwest = "0.11" +ripemd = "0.1.3" rlp = "=0.5.2" rocksdb = "0.21.0" -sea-orm = { version = "0.12.3", features = ["sqlx-postgres", "runtime-tokio-native-tls", "with-bigdecimal", "with-time", "macros"] } -sea-orm-migration = { version = "0.12.3", features = ["sqlx-postgres", "runtime-tokio-native-tls"] } +sea-orm = { version = "0.11.1", features = [ + "sqlx-postgres", + "runtime-tokio-native-tls", + "with-bigdecimal", + "with-time", + "macros", +] } +sea-orm-migration = { version = "0.11.1", features = [ + "sqlx-postgres", + "runtime-tokio-native-tls", +] } semver = "1.0" serde = { version = "1.0", features = ["derive"] } serde_bytes = "0.11" serde_derive = "1.0" serde_json = "1.0" -sha2 = "0.10" +sha2 = { version = "0.10.6", default-features = false } +sha256 = "1.1.4" sha3 = "0.10" solana-account-decoder = "=1.14.13" solana-banks-client = "=1.14.13" @@ -121,7 +146,9 @@ solana-program-test = "=1.14.13" solana-sdk = "=1.14.13" solana-transaction-status = "=1.14.13" solana-zk-token-sdk = "=1.14.13" -spl-associated-token-account = { version = "=1.1.2", features = ["no-entrypoint"] } +spl-associated-token-account = { version = "=1.1.2", features = [ + "no-entrypoint", +] } spl-noop = { version = "=0.1.3", features = ["no-entrypoint"] } spl-token = { version = "=3.5.0", features = ["no-entrypoint"] } spl-token-2022 = { version = "=0.5.0", features = ["no-entrypoint"] } @@ -130,11 +157,15 @@ static_assertions = "1.1" strum = "0.25.0" strum_macros = "0.25.2" tempfile = "3.3" +tendermint = "0.32.2" +tendermint-rpc = { version = "0.32.0", features = ["http-client", "tokio"] } thiserror = "1.0" time = "0.3" tiny-keccak = "2.0.2" tokio = { version = "1", features = ["parking_lot"] } tokio-test = "0.4" +toml_edit = "0.19.14" +tonic = "0.9.2" tracing = { version = "0.1", features = ["release_max_level_debug"] } tracing-error = "0.2" tracing-futures = "0.2" @@ -146,133 +177,145 @@ walkdir = "2" warp = "0.3" which = "4.3" +## TODO: remove this +cosmwasm-schema = "1.2.7" + [workspace.dependencies.ethers] +features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" tag = "2023-06-01" -features = [] [workspace.dependencies.ethers-contract] +features = ["legacy"] git = "https://github.com/hyperlane-xyz/ethers-rs" tag = "2023-06-01" -features = ["legacy"] [workspace.dependencies.ethers-core] +features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" tag = "2023-06-01" -features = [] [workspace.dependencies.ethers-providers] +features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" tag = "2023-06-01" -features = [] [workspace.dependencies.ethers-signers] +features = ["aws"] git = "https://github.com/hyperlane-xyz/ethers-rs" tag = "2023-06-01" -features = ["aws"] [patch.crates-io.curve25519-dalek] -version = "3.2.2" -git = "https://github.com/Eclipse-Laboratories-Inc/curve25519-dalek" branch = "v3.2.2-relax-zeroize" +git = "https://github.com/Eclipse-Laboratories-Inc/curve25519-dalek" +version = "3.2.2" [patch.crates-io.ed25519-dalek] -version = "1.0.1" -git = "https://github.com/Eclipse-Laboratories-Inc/ed25519-dalek" branch = "main" +git = "https://github.com/Eclipse-Laboratories-Inc/ed25519-dalek" +version = "1.0.1" [patch.crates-io.primitive-types] -version = "=0.12.1" -git = "https://github.com/hyperlane-xyz/parity-common.git" branch = "hyperlane" +git = "https://github.com/hyperlane-xyz/parity-common.git" +version = "=0.12.1" [patch.crates-io.rlp] -version = "=0.5.2" -git = "https://github.com/hyperlane-xyz/parity-common.git" branch = "hyperlane" +git = "https://github.com/hyperlane-xyz/parity-common.git" +version = "=0.5.2" [patch.crates-io.solana-account-decoder] -version = "=1.14.13" git = "https://github.com/hyperlane-xyz/solana.git" tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" [patch.crates-io.solana-banks-client] -version = "=1.14.13" git = "https://github.com/hyperlane-xyz/solana.git" tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" [patch.crates-io.solana-banks-interface] -version = "=1.14.13" git = "https://github.com/hyperlane-xyz/solana.git" tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" [patch.crates-io.solana-banks-server] -version = "=1.14.13" git = "https://github.com/hyperlane-xyz/solana.git" tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" [patch.crates-io.solana-clap-utils] -version = "=1.14.13" git = "https://github.com/hyperlane-xyz/solana.git" tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" [patch.crates-io.solana-cli-config] -version = "=1.14.13" git = "https://github.com/hyperlane-xyz/solana.git" tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" [patch.crates-io.solana-client] -version = "=1.14.13" git = "https://github.com/hyperlane-xyz/solana.git" tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" [patch.crates-io.solana-program] -version = "=1.14.13" git = "https://github.com/hyperlane-xyz/solana.git" tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" [patch.crates-io.solana-program-test] -version = "=1.14.13" git = "https://github.com/hyperlane-xyz/solana.git" tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" [patch.crates-io.solana-sdk] -version = "=1.14.13" git = "https://github.com/hyperlane-xyz/solana.git" tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" [patch.crates-io.solana-transaction-status] -version = "=1.14.13" git = "https://github.com/hyperlane-xyz/solana.git" tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" [patch.crates-io.solana-zk-token-sdk] -version = "=1.14.13" git = "https://github.com/hyperlane-xyz/solana.git" tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" [patch.crates-io.spl-associated-token-account] -version = "=1.1.2" -git = "https://github.com/hyperlane-xyz/solana-program-library.git" branch = "hyperlane" +git = "https://github.com/hyperlane-xyz/solana-program-library.git" +version = "=1.1.2" [patch.crates-io.spl-noop] -version = "=0.1.3" -git = "https://github.com/hyperlane-xyz/solana-program-library.git" branch = "hyperlane" +git = "https://github.com/hyperlane-xyz/solana-program-library.git" +version = "=0.1.3" [patch.crates-io.spl-token] -version = "=3.5.0" -git = "https://github.com/hyperlane-xyz/solana-program-library.git" branch = "hyperlane" +git = "https://github.com/hyperlane-xyz/solana-program-library.git" +version = "=3.5.0" [patch.crates-io.spl-token-2022] -version = "=0.5.0" -git = "https://github.com/hyperlane-xyz/solana-program-library.git" branch = "hyperlane" +git = "https://github.com/hyperlane-xyz/solana-program-library.git" +version = "=0.5.0" [patch.crates-io.spl-type-length-value] version = "=0.1.0" - git = "https://github.com/hyperlane-xyz/solana-program-library.git" branch = "hyperlane" + +[patch.crates-io.tendermint] +branch = "trevor/0.32.2-fork" +git = "https://github.com/hyperlane-xyz/tendermint-rs.git" +version = "=0.32.2" + +[patch.crates-io.tendermint-rpc] +branch = "trevor/0.32.2-fork" +git = "https://github.com/hyperlane-xyz/tendermint-rs.git" +version = "=0.32.2" diff --git a/rust/agents/relayer/src/msg/gas_payment/mod.rs b/rust/agents/relayer/src/msg/gas_payment/mod.rs index 132fc286c2..50f010d244 100644 --- a/rust/agents/relayer/src/msg/gas_payment/mod.rs +++ b/rust/agents/relayer/src/msg/gas_payment/mod.rs @@ -88,6 +88,7 @@ impl GasPaymentEnforcer { .db .retrieve_gas_payment_by_gas_payment_key(gas_payment_key)?; let current_expenditure = self.db.retrieve_gas_expenditure_by_message_id(msg_id)?; + for (policy, whitelist) in &self.policies { if !whitelist.msg_matches(message, true) { trace!( diff --git a/rust/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs b/rust/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs index c35971791d..125aa918d5 100644 --- a/rust/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs +++ b/rust/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs @@ -36,13 +36,11 @@ impl MultisigIsmMetadataBuilder for MerkleRootMultisigMetadataBuilder { checkpoint_syncer: &MultisigCheckpointSyncer, ) -> Result> { const CTX: &str = "When fetching MerkleRootMultisig metadata"; - unwrap_or_none_result!( - highest_leaf_index, + let highest_leaf_index = unwrap_or_none_result!( self.highest_known_leaf_index().await, debug!("Couldn't get highest known leaf index") ); - unwrap_or_none_result!( - leaf_index, + let leaf_index = unwrap_or_none_result!( self.get_merkle_leaf_id_by_message_id(message.id()) .await .context(CTX)?, @@ -51,8 +49,7 @@ impl MultisigIsmMetadataBuilder for MerkleRootMultisigMetadataBuilder { "No merkle leaf found for message id, must have not been enqueued in the tree" ) ); - unwrap_or_none_result!( - quorum_checkpoint, + let quorum_checkpoint = unwrap_or_none_result!( checkpoint_syncer .fetch_checkpoint_in_range( validators, diff --git a/rust/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs b/rust/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs index 17374d5ea6..e5feffb85f 100644 --- a/rust/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs +++ b/rust/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs @@ -7,7 +7,7 @@ use derive_new::new; use eyre::{Context, Result}; use hyperlane_base::MultisigCheckpointSyncer; use hyperlane_core::{unwrap_or_none_result, HyperlaneMessage, H256}; -use tracing::{debug, trace, warn}; +use tracing::{debug, warn}; use crate::msg::metadata::BaseMetadataBuilder; @@ -37,8 +37,7 @@ impl MultisigIsmMetadataBuilder for MessageIdMultisigMetadataBuilder { let message_id = message.id(); const CTX: &str = "When fetching MessageIdMultisig metadata"; - unwrap_or_none_result!( - leaf_index, + let leaf_index = unwrap_or_none_result!( self.get_merkle_leaf_id_by_message_id(message_id) .await .context(CTX)?, @@ -47,13 +46,12 @@ impl MultisigIsmMetadataBuilder for MessageIdMultisigMetadataBuilder { "No merkle leaf found for message id, must have not been enqueued in the tree" ) ); - unwrap_or_none_result!( - quorum_checkpoint, + let quorum_checkpoint = unwrap_or_none_result!( checkpoint_syncer .fetch_checkpoint(validators, threshold as usize, leaf_index) .await .context(CTX)?, - trace!("No quorum checkpoint found") + debug!("No quorum checkpoint found") ); if quorum_checkpoint.checkpoint.message_id != message_id { diff --git a/rust/agents/relayer/src/relayer.rs b/rust/agents/relayer/src/relayer.rs index fdccc9c650..5ff91466a2 100644 --- a/rust/agents/relayer/src/relayer.rs +++ b/rust/agents/relayer/src/relayer.rs @@ -9,10 +9,12 @@ use derive_more::AsRef; use eyre::Result; use hyperlane_base::{ db::{HyperlaneRocksDB, DB}, - run_all, BaseAgent, ContractSyncMetrics, CoreMetrics, HyperlaneAgentCore, MessageContractSync, - WatermarkContractSync, + run_all, BaseAgent, ContractSyncMetrics, CoreMetrics, HyperlaneAgentCore, + SequencedDataContractSync, WatermarkContractSync, +}; +use hyperlane_core::{ + HyperlaneDomain, HyperlaneMessage, InterchainGasPayment, MerkleTreeInsertion, U256, }; -use hyperlane_core::{HyperlaneDomain, InterchainGasPayment, MerkleTreeInsertion, U256}; use tokio::{ sync::{ mpsc::{self, UnboundedReceiver, UnboundedSender}, @@ -50,7 +52,7 @@ pub struct Relayer { destination_chains: HashSet, #[as_ref] core: HyperlaneAgentCore, - message_syncs: HashMap>, + message_syncs: HashMap>>, interchain_gas_payment_syncs: HashMap>>, /// Context data for each (origin, destination) chain pair a message can be @@ -58,7 +60,7 @@ pub struct Relayer { msg_ctxs: HashMap>, prover_syncs: HashMap>>, merkle_tree_hook_syncs: - HashMap>>, + HashMap>>, dbs: HashMap, whitelist: Arc, blacklist: Arc, @@ -313,7 +315,9 @@ impl Relayer { ) -> Instrumented>> { let index_settings = self.as_ref().settings.chains[origin.name()].index.clone(); let contract_sync = self.merkle_tree_hook_syncs.get(origin).unwrap().clone(); - let cursor = contract_sync.rate_limited_cursor(index_settings).await; + let cursor = contract_sync + .forward_backward_message_sync_cursor(index_settings) + .await; tokio::spawn(async move { contract_sync.clone().sync("merkle_tree_hook", cursor).await }) .instrument(info_span!("ContractSync")) } diff --git a/rust/agents/scraper/src/chain_scraper/mod.rs b/rust/agents/scraper/src/chain_scraper/mod.rs index 09294905a6..4d91fed5a8 100644 --- a/rust/agents/scraper/src/chain_scraper/mod.rs +++ b/rust/agents/scraper/src/chain_scraper/mod.rs @@ -8,8 +8,8 @@ use eyre::Result; use hyperlane_base::settings::IndexSettings; use hyperlane_core::{ unwrap_or_none_result, BlockInfo, Delivery, HyperlaneDomain, HyperlaneLogStore, - HyperlaneMessage, HyperlaneMessageStore, HyperlaneProvider, HyperlaneWatermarkedLogStore, - InterchainGasPayment, LogMeta, H256, + HyperlaneMessage, HyperlaneProvider, HyperlaneSequenceIndexerStore, + HyperlaneWatermarkedLogStore, InterchainGasPayment, LogMeta, H256, }; use itertools::Itertools; use tracing::trace; @@ -370,26 +370,24 @@ impl HyperlaneLogStore for HyperlaneSqlDb { } #[async_trait] -impl HyperlaneMessageStore for HyperlaneSqlDb { - /// Gets a message by nonce. - async fn retrieve_message_by_nonce(&self, nonce: u32) -> Result> { +impl HyperlaneSequenceIndexerStore for HyperlaneSqlDb { + /// Gets a message by its nonce. + async fn retrieve_by_sequence(&self, sequence: u32) -> Result> { let message = self .db - .retrieve_message_by_nonce(self.domain().id(), &self.mailbox_address, nonce) + .retrieve_message_by_nonce(self.domain().id(), &self.mailbox_address, sequence) .await?; Ok(message) } - /// Retrieves the block number at which the message with the provided nonce - /// was dispatched. - async fn retrieve_dispatched_block_number(&self, nonce: u32) -> Result> { - unwrap_or_none_result!( - tx_id, + /// Gets the block number at which the log occurred. + async fn retrieve_log_block_number(&self, sequence: u32) -> Result> { + let tx_id = unwrap_or_none_result!( self.db - .retrieve_dispatched_tx_id(self.domain().id(), &self.mailbox_address, nonce) + .retrieve_dispatched_tx_id(self.domain().id(), &self.mailbox_address, sequence) .await? ); - unwrap_or_none_result!(block_id, self.db.retrieve_block_id(tx_id).await?); + let block_id = unwrap_or_none_result!(self.db.retrieve_block_id(tx_id).await?); Ok(self.db.retrieve_block_number(block_id).await?) } } diff --git a/rust/agents/validator/Cargo.toml b/rust/agents/validator/Cargo.toml index 4607a1a9b3..f562938db2 100644 --- a/rust/agents/validator/Cargo.toml +++ b/rust/agents/validator/Cargo.toml @@ -27,10 +27,12 @@ tracing.workspace = true hyperlane-core = { path = "../../hyperlane-core", features = ["agent"] } hyperlane-base = { path = "../../hyperlane-base" } hyperlane-ethereum = { path = "../../chains/hyperlane-ethereum" } +hyperlane-cosmos = { path = "../../chains/hyperlane-cosmos" } [dev-dependencies] tokio-test.workspace = true hyperlane-test = { path = "../../hyperlane-test" } +k256.workspace = true [features] default = ["color-eyre", "oneline-errors"] diff --git a/rust/agents/validator/src/settings.rs b/rust/agents/validator/src/settings.rs index 4c2c673b2a..f6870a31c5 100644 --- a/rust/agents/validator/src/settings.rs +++ b/rust/agents/validator/src/settings.rs @@ -65,7 +65,8 @@ impl FromRawConf for ValidatorSettings { .end(); let origin_chain_name_set = origin_chain_name.map(|s| HashSet::from([s])); - let base = p + + let base: Option = p .parse_from_raw_config::>>( origin_chain_name_set.as_ref(), "Expected valid base agent configuration", diff --git a/rust/agents/validator/src/validator.rs b/rust/agents/validator/src/validator.rs index d516e051c0..96feb97b8f 100644 --- a/rust/agents/validator/src/validator.rs +++ b/rust/agents/validator/src/validator.rs @@ -4,19 +4,22 @@ use async_trait::async_trait; use derive_more::AsRef; use eyre::Result; use futures_util::future::ready; + +use tokio::{task::JoinHandle, time::sleep}; +use tracing::{error, info, info_span, instrument::Instrumented, warn, Instrument}; + use hyperlane_base::{ db::{HyperlaneRocksDB, DB}, run_all, BaseAgent, CheckpointSyncer, ContractSyncMetrics, CoreMetrics, HyperlaneAgentCore, - WatermarkContractSync, + SequencedDataContractSync, }; + use hyperlane_core::{ Announcement, ChainResult, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneSigner, HyperlaneSignerExt, Mailbox, MerkleTreeHook, MerkleTreeInsertion, TxOutcome, ValidatorAnnounce, H256, U256, }; use hyperlane_ethereum::{SingletonSigner, SingletonSignerHandle}; -use tokio::{task::JoinHandle, time::sleep}; -use tracing::{error, info, info_span, instrument::Instrumented, warn, Instrument}; use crate::{ settings::ValidatorSettings, @@ -30,7 +33,7 @@ pub struct Validator { #[as_ref] core: HyperlaneAgentCore, db: HyperlaneRocksDB, - merkle_tree_hook_sync: Arc>, + merkle_tree_hook_sync: Arc>, mailbox: Arc, merkle_tree_hook: Arc, validator_announce: Arc, @@ -151,7 +154,9 @@ impl Validator { let index_settings = self.as_ref().settings.chains[self.origin_chain.name()].index_settings(); let contract_sync = self.merkle_tree_hook_sync.clone(); - let cursor = contract_sync.rate_limited_cursor(index_settings).await; + let cursor = contract_sync + .forward_backward_message_sync_cursor(index_settings) + .await; tokio::spawn(async move { contract_sync.clone().sync("merkle_tree_hook", cursor).await }) .instrument(info_span!("MerkleTreeHookSyncer")) } @@ -199,14 +204,21 @@ impl Validator { tasks } - fn log_on_announce_failure(result: ChainResult) { + fn log_on_announce_failure(result: ChainResult, chain_signer: &String) { match result { Ok(outcome) => { - if !outcome.executed { + if outcome.executed { + info!( + tx_outcome=?outcome, + ?chain_signer, + "Successfully announced validator", + ); + } else { error!( txid=?outcome.transaction_id, gas_used=?outcome.gas_used, gas_price=?outcome.gas_price, + ?chain_signer, "Transaction attempting to announce validator reverted. Make sure you have enough funds in your account to pay for transaction fees." ); } @@ -214,19 +226,23 @@ impl Validator { Err(err) => { error!( ?err, - "Failed to announce validator. Make sure you have enough ETH in your account to pay for gas." + ?chain_signer, + "Failed to announce validator. Make sure you have enough funds in your account to pay for gas." ); } } } async fn announce(&self) -> Result<()> { + let address = self.signer.eth_address(); + let announcement_location = self.checkpoint_syncer.announcement_location(); + // Sign and post the validator announcement let announcement = Announcement { - validator: self.signer.eth_address(), + validator: address, mailbox_address: self.mailbox.address(), mailbox_domain: self.mailbox.domain().id(), - storage_location: self.checkpoint_syncer.announcement_location(), + storage_location: announcement_location.clone(), }; let signed_announcement = self.signer.sign(announcement.clone()).await?; self.checkpoint_syncer @@ -237,7 +253,7 @@ impl Validator { // the main validator submit loop. This is to avoid a situation in // which the validator is signing checkpoints but has not announced // their locations, which makes them functionally unusable. - let validators: [H256; 1] = [self.signer.eth_address().into()]; + let validators: [H256; 1] = [address.into()]; loop { info!("Checking for validator announcement"); if let Some(locations) = self @@ -246,8 +262,12 @@ impl Validator { .await? .first() { - if locations.contains(&self.checkpoint_syncer.announcement_location()) { - info!("Validator has announced signature storage location"); + if locations.contains(&announcement_location) { + info!( + ?locations, + ?announcement_location, + "Validator has announced signature storage location" + ); break; } info!( @@ -255,10 +275,12 @@ impl Validator { "Validator has not announced signature storage location" ); - if self.core.settings.chains[self.origin_chain.name()] - .signer - .is_some() + if let Some(chain_signer) = self.core.settings.chains[self.origin_chain.name()] + .chain_signer() + .await? { + let chain_signer = chain_signer.address_string(); + info!(eth_validator_address=?announcement.validator, ?chain_signer, "Attempting self announce"); let balance_delta = self .validator_announce .announce_tokens_needed(signed_announcement.clone()) @@ -267,15 +289,16 @@ impl Validator { if balance_delta > U256::zero() { warn!( tokens_needed=%balance_delta, - validator_address=?announcement.validator, - "Please send tokens to the validator address to announce", + eth_validator_address=?announcement.validator, + ?chain_signer, + "Please send tokens to your chain signer address to announce", ); } else { let result = self .validator_announce .announce(signed_announcement.clone(), None) .await; - Self::log_on_announce_failure(result); + Self::log_on_announce_failure(result, &chain_signer); } } else { warn!(origin_chain=%self.origin_chain, "Cannot announce validator without a signer; make sure a signer is set for the origin chain"); @@ -287,6 +310,3 @@ impl Validator { Ok(()) } } - -#[cfg(test)] -mod test {} diff --git a/rust/chains/hyperlane-cosmos/Cargo.toml b/rust/chains/hyperlane-cosmos/Cargo.toml new file mode 100644 index 0000000000..12b084ba93 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "hyperlane-cosmos" +documentation = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license-file = { workspace = true } +publish = { workspace = true } +version = { workspace = true } + +[dependencies] +async-trait = { workspace = true } +base64 = { workspace = true } +bech32 = { workspace = true } +cosmrs = { workspace = true, features = ["cosmwasm", "tokio", "grpc", "rpc"] } +derive-new = { workspace = true } +hex = { workspace = true } +hpl-interface.workspace = true +hyper = { workspace = true } +hyper-tls = { workspace = true } +once_cell = { workspace = true } +ripemd = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +sha2 = { workspace = true } +sha256 = { workspace = true } +tendermint = { workspace = true, features = ["rust-crypto", "secp256k1"] } +tendermint-rpc = { workspace = true } +thiserror = { workspace = true } +tokio = { workspace = true } +tonic = { workspace = true } +tracing = { workspace = true } +tracing-futures = { workspace = true } +url = { workspace = true } + +hyperlane-core = { path = "../../hyperlane-core" } diff --git a/rust/chains/hyperlane-cosmos/src/aggregation_ism.rs b/rust/chains/hyperlane-cosmos/src/aggregation_ism.rs new file mode 100644 index 0000000000..c9d7200117 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/aggregation_ism.rs @@ -0,0 +1,77 @@ +use std::str::FromStr; + +use crate::{ + address::CosmosAddress, + grpc::{WasmGrpcProvider, WasmProvider}, + payloads::aggregate_ism::{ModulesAndThresholdRequest, ModulesAndThresholdResponse}, + ConnectionConf, CosmosProvider, Signer, +}; +use async_trait::async_trait; +use hyperlane_core::{ + AggregationIsm, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, + HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, H256, +}; +use tracing::instrument; + +/// A reference to an AggregationIsm contract on some Cosmos chain +#[derive(Debug)] +pub struct CosmosAggregationIsm { + domain: HyperlaneDomain, + address: H256, + provider: Box, +} + +impl CosmosAggregationIsm { + /// create new Cosmos AggregationIsm agent + pub fn new( + conf: ConnectionConf, + locator: ContractLocator, + signer: Option, + ) -> ChainResult { + let provider = WasmGrpcProvider::new(conf.clone(), locator.clone(), signer)?; + + Ok(Self { + domain: locator.domain.clone(), + address: locator.address, + provider: Box::new(provider), + }) + } +} + +impl HyperlaneContract for CosmosAggregationIsm { + fn address(&self) -> H256 { + self.address + } +} + +impl HyperlaneChain for CosmosAggregationIsm { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(CosmosProvider::new(self.domain.clone())) + } +} + +#[async_trait] +impl AggregationIsm for CosmosAggregationIsm { + #[instrument(err)] + async fn modules_and_threshold( + &self, + message: &HyperlaneMessage, + ) -> ChainResult<(Vec, u8)> { + let payload = ModulesAndThresholdRequest::new(message); + + let data = self.provider.wasm_query(payload, None).await?; + let response: ModulesAndThresholdResponse = serde_json::from_slice(&data)?; + + let modules: ChainResult> = response + .modules + .into_iter() + .map(|module| CosmosAddress::from_str(&module).map(|ca| ca.digest())) + .collect(); + + Ok((modules?, response.threshold)) + } +} diff --git a/rust/chains/hyperlane-cosmos/src/error.rs b/rust/chains/hyperlane-cosmos/src/error.rs new file mode 100644 index 0000000000..92af0bce05 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/error.rs @@ -0,0 +1,43 @@ +use cosmrs::proto::prost; +use hyperlane_core::ChainCommunicationError; + +/// Errors from the crates specific to the hyperlane-cosmos +/// implementation. +/// This error can then be converted into the broader error type +/// in hyperlane-core using the `From` trait impl +#[derive(Debug, thiserror::Error)] +pub enum HyperlaneCosmosError { + /// base64 error + #[error("{0}")] + Base64(#[from] base64::DecodeError), + /// bech32 error + #[error("{0}")] + Bech32(#[from] bech32::Error), + /// gRPC error + #[error("{0}")] + GrpcError(#[from] tonic::Status), + /// Cosmos error + #[error("{0}")] + CosmosError(#[from] cosmrs::Error), + /// Cosmos error report + #[error("{0}")] + CosmosErrorReport(#[from] cosmrs::ErrorReport), + #[error("{0}")] + /// Cosmrs Tendermint Error + CosmrsTendermintError(#[from] cosmrs::tendermint::Error), + /// Tonic error + #[error("{0}")] + Tonic(#[from] tonic::transport::Error), + /// Tendermint RPC Error + #[error(transparent)] + TendermintError(#[from] tendermint_rpc::error::Error), + /// protobuf error + #[error("{0}")] + Protobuf(#[from] prost::DecodeError), +} + +impl From for ChainCommunicationError { + fn from(value: HyperlaneCosmosError) -> Self { + ChainCommunicationError::from_other(value) + } +} diff --git a/rust/chains/hyperlane-cosmos/src/interchain_gas.rs b/rust/chains/hyperlane-cosmos/src/interchain_gas.rs new file mode 100644 index 0000000000..d96bfb0bab --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/interchain_gas.rs @@ -0,0 +1,302 @@ +use async_trait::async_trait; +use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; +use cosmrs::tendermint::abci::EventAttribute; +use hyperlane_core::{ + ChainCommunicationError, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, + HyperlaneDomain, HyperlaneProvider, Indexer, InterchainGasPaymaster, InterchainGasPayment, + LogMeta, SequenceIndexer, H256, U256, +}; +use once_cell::sync::Lazy; +use std::ops::RangeInclusive; + +use crate::{ + grpc::WasmGrpcProvider, + rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer}, + signers::Signer, + utils::{CONTRACT_ADDRESS_ATTRIBUTE_KEY, CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64}, + ConnectionConf, CosmosProvider, HyperlaneCosmosError, +}; + +/// A reference to a InterchainGasPaymaster contract on some Cosmos chain +#[derive(Debug)] +pub struct CosmosInterchainGasPaymaster { + domain: HyperlaneDomain, + address: H256, +} + +impl HyperlaneContract for CosmosInterchainGasPaymaster { + fn address(&self) -> H256 { + self.address + } +} + +impl HyperlaneChain for CosmosInterchainGasPaymaster { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(CosmosProvider::new(self.domain.clone())) + } +} + +impl InterchainGasPaymaster for CosmosInterchainGasPaymaster {} + +impl CosmosInterchainGasPaymaster { + /// create new Cosmos InterchainGasPaymaster agent + pub fn new( + conf: ConnectionConf, + locator: ContractLocator, + signer: Option, + ) -> ChainResult { + let provider = WasmGrpcProvider::new(conf.clone(), locator.clone(), signer)?; + + Ok(Self { + domain: locator.domain.clone(), + address: locator.address, + }) + } +} + +// ------------------ Indexer ------------------ + +const MESSAGE_ID_ATTRIBUTE_KEY: &str = "message_id"; +static MESSAGE_ID_ATTRIBUTE_KEY_BASE64: Lazy = + Lazy::new(|| BASE64.encode(MESSAGE_ID_ATTRIBUTE_KEY)); + +const PAYMENT_ATTRIBUTE_KEY: &str = "payment"; +static PAYMENT_ATTRIBUTE_KEY_BASE64: Lazy = + Lazy::new(|| BASE64.encode(PAYMENT_ATTRIBUTE_KEY)); + +const GAS_AMOUNT_ATTRIBUTE_KEY: &str = "gas_amount"; +static GAS_AMOUNT_ATTRIBUTE_KEY_BASE64: Lazy = + Lazy::new(|| BASE64.encode(GAS_AMOUNT_ATTRIBUTE_KEY)); + +const DESTINATION_ATTRIBUTE_KEY: &str = "dest_domain"; +static DESTINATION_ATTRIBUTE_KEY_BASE64: Lazy = + Lazy::new(|| BASE64.encode(DESTINATION_ATTRIBUTE_KEY)); + +/// A reference to a InterchainGasPaymasterIndexer contract on some Cosmos chain +#[derive(Debug)] +pub struct CosmosInterchainGasPaymasterIndexer { + indexer: Box, +} + +impl CosmosInterchainGasPaymasterIndexer { + /// The interchain gas payment event type from the CW contract. + const INTERCHAIN_GAS_PAYMENT_EVENT_TYPE: &str = "igp-core-pay-for-gas"; + + /// create new Cosmos InterchainGasPaymasterIndexer agent + pub fn new( + conf: ConnectionConf, + locator: ContractLocator, + reorg_period: u32, + ) -> ChainResult { + let indexer = CosmosWasmIndexer::new( + conf, + locator, + Self::INTERCHAIN_GAS_PAYMENT_EVENT_TYPE.into(), + reorg_period, + )?; + + Ok(Self { + indexer: Box::new(indexer), + }) + } + + fn interchain_gas_payment_parser( + attrs: &Vec, + ) -> ChainResult> { + let mut contract_address: Option = None; + let mut gas_payment = IncompleteInterchainGasPayment::default(); + + for attr in attrs { + let key = attr.key.as_str(); + let value = attr.value.as_str(); + + match key { + CONTRACT_ADDRESS_ATTRIBUTE_KEY => { + contract_address = Some(value.to_string()); + } + v if *CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64 == v => { + contract_address = Some(String::from_utf8( + BASE64 + .decode(value) + .map_err(Into::::into)?, + )?); + } + + MESSAGE_ID_ATTRIBUTE_KEY => { + gas_payment.message_id = Some(H256::from_slice(hex::decode(value)?.as_slice())); + } + v if *MESSAGE_ID_ATTRIBUTE_KEY_BASE64 == v => { + gas_payment.message_id = Some(H256::from_slice( + hex::decode(String::from_utf8( + BASE64 + .decode(value) + .map_err(Into::::into)?, + )?)? + .as_slice(), + )); + } + + PAYMENT_ATTRIBUTE_KEY => { + gas_payment.payment = Some(U256::from_dec_str(value)?); + } + v if *PAYMENT_ATTRIBUTE_KEY_BASE64 == v => { + let dec_str = String::from_utf8( + BASE64 + .decode(value) + .map_err(Into::::into)?, + )?; + // U256's from_str assumes a radix of 16, so we explicitly use from_dec_str. + gas_payment.payment = Some(U256::from_dec_str(dec_str.as_str())?); + } + + GAS_AMOUNT_ATTRIBUTE_KEY => { + gas_payment.gas_amount = Some(U256::from_dec_str(value)?); + } + v if *GAS_AMOUNT_ATTRIBUTE_KEY_BASE64 == v => { + let dec_str = String::from_utf8( + BASE64 + .decode(value) + .map_err(Into::::into)?, + )?; + // U256's from_str assumes a radix of 16, so we explicitly use from_dec_str. + gas_payment.gas_amount = Some(U256::from_dec_str(dec_str.as_str())?); + } + + DESTINATION_ATTRIBUTE_KEY => { + gas_payment.destination = Some(value.parse::()?); + } + v if *DESTINATION_ATTRIBUTE_KEY_BASE64 == v => { + gas_payment.destination = Some( + String::from_utf8( + BASE64 + .decode(value) + .map_err(Into::::into)?, + )? + .parse()?, + ); + } + + _ => {} + } + } + + let contract_address = contract_address + .ok_or_else(|| ChainCommunicationError::from_other_str("missing contract_address"))?; + + Ok(ParsedEvent::new(contract_address, gas_payment.try_into()?)) + } +} + +#[async_trait] +impl Indexer for CosmosInterchainGasPaymasterIndexer { + async fn fetch_logs( + &self, + range: RangeInclusive, + ) -> ChainResult> { + let result = self + .indexer + .get_range_event_logs(range, Self::interchain_gas_payment_parser) + .await?; + Ok(result) + } + + async fn get_finalized_block_number(&self) -> ChainResult { + self.indexer.get_finalized_block_number().await + } +} + +#[async_trait] +impl SequenceIndexer for CosmosInterchainGasPaymasterIndexer { + async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { + // TODO: implement when cosmwasm scraper support is implemented + let tip = self.get_finalized_block_number().await?; + Ok((None, tip)) + } +} + +#[derive(Default)] +struct IncompleteInterchainGasPayment { + message_id: Option, + payment: Option, + gas_amount: Option, + destination: Option, +} + +impl TryInto for IncompleteInterchainGasPayment { + type Error = ChainCommunicationError; + + fn try_into(self) -> Result { + let message_id = self + .message_id + .ok_or_else(|| ChainCommunicationError::from_other_str("missing message_id"))?; + let payment = self + .payment + .ok_or_else(|| ChainCommunicationError::from_other_str("missing payment"))?; + let gas_amount = self + .gas_amount + .ok_or_else(|| ChainCommunicationError::from_other_str("missing gas_amount"))?; + let destination = self + .destination + .ok_or_else(|| ChainCommunicationError::from_other_str("missing destination"))?; + + Ok(InterchainGasPayment { + message_id, + payment, + gas_amount, + destination, + }) + } +} + +#[cfg(test)] +mod tests { + use cosmrs::tendermint::abci::EventAttribute; + use hyperlane_core::{InterchainGasPayment, H256, U256}; + use std::str::FromStr; + + use crate::{rpc::ParsedEvent, utils::event_attributes_from_str}; + + use super::*; + + #[test] + fn test_interchain_gas_payment_parser() { + // Examples from https://rpc-kralum.neutron-1.neutron.org/tx_search?query=%22tx.height%20%3E=%204000000%20AND%20tx.height%20%3C=%204100000%20AND%20wasm-igp-core-pay-for-gas._contract_address%20=%20%27neutron12p8wntzra3vpfcqv05scdx5sa3ftaj6gjcmtm7ynkl0e6crtt4ns8cnrmx%27%22&prove=false&page=1&per_page=100 + + let expected = ParsedEvent::new( + "neutron12p8wntzra3vpfcqv05scdx5sa3ftaj6gjcmtm7ynkl0e6crtt4ns8cnrmx".into(), + InterchainGasPayment { + message_id: H256::from_str( + "5dcf6120f8adf4f267eb1a122a85c42eae257fbc872671e93929fbf63daed19b", + ) + .unwrap(), + payment: U256::from(2), + gas_amount: U256::from(25000), + destination: 169, + }, + ); + + let assert_parsed_event = |attrs: &Vec| { + let parsed_event = + CosmosInterchainGasPaymasterIndexer::interchain_gas_payment_parser(attrs).unwrap(); + + assert_eq!(parsed_event, expected); + }; + + // Non-base64 version + let non_base64_attrs = event_attributes_from_str( + r#"[{"key":"_contract_address","value":"neutron12p8wntzra3vpfcqv05scdx5sa3ftaj6gjcmtm7ynkl0e6crtt4ns8cnrmx", "index": true},{"key":"dest_domain","value":"169", "index": true},{"key":"gas_amount","value":"25000", "index": true},{"key":"gas_refunded","value":"0", "index": true},{"key":"gas_required","value":"2", "index": true},{"key":"message_id","value":"5dcf6120f8adf4f267eb1a122a85c42eae257fbc872671e93929fbf63daed19b", "index": true},{"key":"payment","value":"2", "index": true},{"key":"sender","value":"neutron1vdazwhwkh9wy6ue66pjpuvrxcrywv2ww956dq6ls2gh0n7t9f5rs2hydt2", "index": true}]"#, + ); + assert_parsed_event(&non_base64_attrs); + + // Base64 version + let base64_attrs = event_attributes_from_str( + r#"[{"key":"X2NvbnRyYWN0X2FkZHJlc3M=","value":"bmV1dHJvbjEycDh3bnR6cmEzdnBmY3F2MDVzY2R4NXNhM2Z0YWo2Z2pjbXRtN3lua2wwZTZjcnR0NG5zOGNucm14","index":true},{"key":"ZGVzdF9kb21haW4=","value":"MTY5","index":true},{"key":"Z2FzX2Ftb3VudA==","value":"MjUwMDA=","index":true},{"key":"Z2FzX3JlZnVuZGVk","value":"MA==","index":true},{"key":"Z2FzX3JlcXVpcmVk","value":"Mg==","index":true},{"key":"bWVzc2FnZV9pZA==","value":"NWRjZjYxMjBmOGFkZjRmMjY3ZWIxYTEyMmE4NWM0MmVhZTI1N2ZiYzg3MjY3MWU5MzkyOWZiZjYzZGFlZDE5Yg==","index":true},{"key":"cGF5bWVudA==","value":"Mg==","index":true},{"key":"c2VuZGVy","value":"bmV1dHJvbjF2ZGF6d2h3a2g5d3k2dWU2NnBqcHV2cnhjcnl3djJ3dzk1NmRxNmxzMmdoMG43dDlmNXJzMmh5ZHQy","index":true}]"#, + ); + + assert_parsed_event(&base64_attrs); + } +} diff --git a/rust/chains/hyperlane-cosmos/src/interchain_security_module.rs b/rust/chains/hyperlane-cosmos/src/interchain_security_module.rs new file mode 100644 index 0000000000..72a0ac984d --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/interchain_security_module.rs @@ -0,0 +1,91 @@ +use async_trait::async_trait; +use hyperlane_core::{ + ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + HyperlaneMessage, HyperlaneProvider, InterchainSecurityModule, ModuleType, H256, U256, +}; + +use crate::{ + grpc::{WasmGrpcProvider, WasmProvider}, + payloads::{ + general::EmptyStruct, + ism_routes::{QueryIsmGeneralRequest, QueryIsmModuleTypeRequest}, + }, + types::IsmType, + ConnectionConf, CosmosProvider, Signer, +}; + +#[derive(Debug)] +/// The Cosmos Interchain Security Module. +pub struct CosmosInterchainSecurityModule { + /// The domain of the ISM contract. + domain: HyperlaneDomain, + /// The address of the ISM contract. + address: H256, + /// The provider for the ISM contract. + provider: Box, +} + +/// The Cosmos Interchain Security Module Implementation. +impl CosmosInterchainSecurityModule { + /// Creates a new Cosmos Interchain Security Module. + pub fn new( + conf: &ConnectionConf, + locator: ContractLocator, + signer: Option, + ) -> ChainResult { + let provider: WasmGrpcProvider = + WasmGrpcProvider::new(conf.clone(), locator.clone(), signer)?; + + Ok(Self { + domain: locator.domain.clone(), + address: locator.address, + provider: Box::new(provider), + }) + } +} + +impl HyperlaneContract for CosmosInterchainSecurityModule { + fn address(&self) -> H256 { + self.address + } +} + +impl HyperlaneChain for CosmosInterchainSecurityModule { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(CosmosProvider::new(self.domain.clone())) + } +} + +#[async_trait] +impl InterchainSecurityModule for CosmosInterchainSecurityModule { + /// Returns the module type of the ISM compliant with the corresponding + /// metadata offchain fetching and onchain formatting standard. + async fn module_type(&self) -> ChainResult { + let query = QueryIsmModuleTypeRequest { + module_type: EmptyStruct {}, + }; + + let data = self + .provider + .wasm_query(QueryIsmGeneralRequest { ism: query }, None) + .await?; + + let module_type_response = + serde_json::from_slice::(&data)?; + Ok(IsmType(module_type_response.typ).into()) + } + + /// Dry runs the `verify()` ISM call and returns `Some(gas_estimate)` if the call + /// succeeds. + async fn dry_run_verify( + &self, + message: &HyperlaneMessage, + metadata: &[u8], + ) -> ChainResult> { + Ok(Some(U256::from(1000))) // TODO + } +} diff --git a/rust/chains/hyperlane-cosmos/src/lib.rs b/rust/chains/hyperlane-cosmos/src/lib.rs new file mode 100644 index 0000000000..82a4a0ece1 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/lib.rs @@ -0,0 +1,29 @@ +//! Implementation of hyperlane for cosmos. + +#![forbid(unsafe_code)] +#![warn(missing_docs)] +// TODO: Remove once we start filling things in +#![allow(unused_variables)] + +mod aggregation_ism; +mod error; +mod interchain_gas; +mod interchain_security_module; +mod libs; +mod mailbox; +mod merkle_tree_hook; +mod multisig_ism; +mod payloads; +mod providers; +mod routing_ism; +mod signers; +mod trait_builder; +mod types; +mod utils; +mod validator_announce; + +pub use self::{ + aggregation_ism::*, error::*, interchain_gas::*, interchain_security_module::*, libs::*, + mailbox::*, merkle_tree_hook::*, multisig_ism::*, providers::*, routing_ism::*, signers::*, + trait_builder::*, trait_builder::*, validator_announce::*, validator_announce::*, +}; diff --git a/rust/chains/hyperlane-cosmos/src/libs/address.rs b/rust/chains/hyperlane-cosmos/src/libs/address.rs new file mode 100644 index 0000000000..d5970b9b82 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/libs/address.rs @@ -0,0 +1,148 @@ +use std::str::FromStr; + +use cosmrs::{ + crypto::{secp256k1::SigningKey, PublicKey}, + AccountId, +}; +use derive_new::new; +use hyperlane_core::{ChainCommunicationError, ChainResult, Error::Overflow, H256}; +use tendermint::account::Id as TendermintAccountId; +use tendermint::public_key::PublicKey as TendermintPublicKey; + +use crate::HyperlaneCosmosError; + +/// Wrapper around the cosmrs AccountId type that abstracts bech32 encoding +#[derive(new, Debug)] +pub struct CosmosAddress { + /// Bech32 encoded cosmos account + account_id: AccountId, + /// Hex representation (digest) of cosmos account + digest: H256, +} + +impl CosmosAddress { + /// Returns a Bitcoin style address: RIPEMD160(SHA256(pubkey)) + /// Source: https://github.com/cosmos/cosmos-sdk/blob/177e7f45959215b0b4e85babb7c8264eaceae052/crypto/keys/secp256k1/secp256k1.go#L154 + pub fn from_pubkey(pubkey: PublicKey, prefix: &str) -> ChainResult { + // Get the inner type + let tendermint_pubkey = TendermintPublicKey::from(pubkey); + // Get the RIPEMD160(SHA256(pubkey)) + let tendermint_id = TendermintAccountId::from(tendermint_pubkey); + // Bech32 encoding + let account_id = AccountId::new(prefix, tendermint_id.as_bytes()) + .map_err(Into::::into)?; + // Hex digest + let digest = Self::bech32_decode(account_id.clone())?; + Ok(CosmosAddress::new(account_id, digest)) + } + + /// Creates a wrapper arround a cosmrs AccountId from a private key byte array + pub fn from_privkey(priv_key: &[u8], prefix: &str) -> ChainResult { + let pubkey = SigningKey::from_slice(priv_key) + .map_err(Into::::into)? + .public_key(); + Self::from_pubkey(pubkey, prefix) + } + + /// Creates a wrapper arround a cosmrs AccountId from a H256 digest + /// + /// - digest: H256 digest (hex representation of address) + /// - prefix: Bech32 prefix + pub fn from_h256(digest: H256, prefix: &str) -> ChainResult { + // This is the hex-encoded version of the address + let bytes = digest.as_bytes(); + // Bech32 encode it + let account_id = + AccountId::new(prefix, bytes).map_err(Into::::into)?; + Ok(CosmosAddress::new(account_id, digest)) + } + + /// Builds a H256 digest from a cosmos AccountId (Bech32 encoding) + fn bech32_decode(account_id: AccountId) -> ChainResult { + // Temporarily set the digest to a default value as a placeholder. + // Can't implement H256::try_from for AccountId to avoid this. + let cosmos_address = CosmosAddress::new(account_id, Default::default()); + H256::try_from(&cosmos_address) + } + + /// String representation of a cosmos AccountId + pub fn address(&self) -> String { + self.account_id.to_string() + } + + /// H256 digest of the cosmos AccountId + pub fn digest(&self) -> H256 { + self.digest + } +} + +impl TryFrom<&CosmosAddress> for H256 { + type Error = ChainCommunicationError; + + fn try_from(cosmos_address: &CosmosAddress) -> Result { + // `to_bytes()` decodes the Bech32 into a hex, represented as a byte vec + let bytes = cosmos_address.account_id.to_bytes(); + let h256_len = H256::len_bytes(); + let Some(start_point) = h256_len.checked_sub(bytes.len()) else { + // input is too large to fit in a H256 + return Err(Overflow.into()); + }; + let mut empty_hash = H256::default(); + let result = empty_hash.as_bytes_mut(); + result[start_point..].copy_from_slice(bytes.as_slice()); + Ok(H256::from_slice(result)) + } +} + +impl FromStr for CosmosAddress { + type Err = ChainCommunicationError; + + fn from_str(s: &str) -> Result { + let account_id = AccountId::from_str(s).map_err(Into::::into)?; + let digest = Self::bech32_decode(account_id.clone())?; + Ok(Self::new(account_id, digest)) + } +} + +#[cfg(test)] +pub mod test { + use hyperlane_core::utils::hex_or_base58_to_h256; + + use super::*; + + #[test] + fn test_bech32_decode() { + let addr = "dual1pk99xge6q94qtu3568x3qhp68zzv0mx7za4ct008ks36qhx5tvss3qawfh"; + let cosmos_address = CosmosAddress::from_str(addr).unwrap(); + assert_eq!( + cosmos_address.digest, + H256::from_str("0d8a53233a016a05f234d1cd105c3a3884c7ecde176b85bde7b423a05cd45b21") + .unwrap() + ); + } + + #[test] + fn test_bech32_decode_from_cosmos_key() { + let hex_key = "0x5486418967eabc770b0fcb995f7ef6d9a72f7fc195531ef76c5109f44f51af26"; + let key = hex_or_base58_to_h256(hex_key).unwrap(); + let prefix = "neutron"; + let addr = CosmosAddress::from_privkey(key.as_bytes(), prefix) + .expect("Cosmos address creation failed"); + assert_eq!( + addr.address(), + "neutron1kknekjxg0ear00dky5ykzs8wwp2gz62z9s6aaj" + ); + } + + #[test] + fn test_bech32_encode_from_h256() { + let hex_key = "0x1b16866227825a5166eb44031cdcf6568b3e80b52f2806e01b89a34dc90ae616"; + let key = hex_or_base58_to_h256(hex_key).unwrap(); + let prefix = "dual"; + let addr = CosmosAddress::from_h256(key, prefix).expect("Cosmos address creation failed"); + assert_eq!( + addr.address(), + "dual1rvtgvc38sfd9zehtgsp3eh8k269naq949u5qdcqm3x35mjg2uctqfdn3yq" + ); + } +} diff --git a/rust/chains/hyperlane-cosmos/src/libs/mod.rs b/rust/chains/hyperlane-cosmos/src/libs/mod.rs new file mode 100644 index 0000000000..d89e6cd7db --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/libs/mod.rs @@ -0,0 +1,2 @@ +/// This module contains all the verification variables the libraries used by the Hyperlane Cosmos chain. +pub mod address; diff --git a/rust/chains/hyperlane-cosmos/src/mailbox.rs b/rust/chains/hyperlane-cosmos/src/mailbox.rs new file mode 100644 index 0000000000..4df968edc9 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/mailbox.rs @@ -0,0 +1,417 @@ +use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; +use std::{ + fmt::{Debug, Formatter}, + io::Cursor, + num::NonZeroU64, + ops::RangeInclusive, + str::FromStr, +}; + +use crate::payloads::mailbox::{ + GeneralMailboxQuery, ProcessMessageRequest, ProcessMessageRequestInner, +}; +use crate::payloads::{general, mailbox}; +use crate::rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer}; +use crate::CosmosProvider; +use crate::{address::CosmosAddress, types::tx_response_to_outcome}; +use crate::{ + grpc::{WasmGrpcProvider, WasmProvider}, + HyperlaneCosmosError, +}; +use crate::{signers::Signer, utils::get_block_height_for_lag, ConnectionConf}; +use async_trait::async_trait; +use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; +use cosmrs::tendermint::abci::EventAttribute; +use once_cell::sync::Lazy; + +use crate::utils::{CONTRACT_ADDRESS_ATTRIBUTE_KEY, CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64}; +use hyperlane_core::{ + utils::fmt_bytes, ChainResult, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + HyperlaneMessage, HyperlaneProvider, Indexer, LogMeta, Mailbox, TxCostEstimate, TxOutcome, + H256, U256, +}; +use hyperlane_core::{ + ChainCommunicationError, ContractLocator, Decode, RawHyperlaneMessage, SequenceIndexer, +}; +use tracing::{instrument, warn}; + +/// A reference to a Mailbox contract on some Cosmos chain +pub struct CosmosMailbox { + config: ConnectionConf, + domain: HyperlaneDomain, + address: H256, + provider: Box, +} + +impl CosmosMailbox { + /// Create a reference to a mailbox at a specific Ethereum address on some + /// chain + pub fn new( + conf: ConnectionConf, + locator: ContractLocator, + signer: Option, + ) -> ChainResult { + let provider = WasmGrpcProvider::new(conf.clone(), locator.clone(), signer)?; + + Ok(Self { + config: conf, + domain: locator.domain.clone(), + address: locator.address, + provider: Box::new(provider), + }) + } + + /// Prefix used in the bech32 address encoding + pub fn prefix(&self) -> String { + self.config.get_prefix() + } +} + +impl HyperlaneContract for CosmosMailbox { + fn address(&self) -> H256 { + self.address + } +} + +impl HyperlaneChain for CosmosMailbox { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(CosmosProvider::new(self.domain.clone())) + } +} + +impl Debug for CosmosMailbox { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + // Debug::fmt(&(self as &dyn HyperlaneContract), f) + todo!() + } +} + +#[async_trait] +impl Mailbox for CosmosMailbox { + #[instrument(level = "debug", err, ret, skip(self))] + async fn count(&self, lag: Option) -> ChainResult { + let block_height = get_block_height_for_lag(&self.provider, lag).await?; + self.nonce_at_block(block_height).await + } + + #[instrument(level = "debug", err, ret, skip(self))] + async fn delivered(&self, id: H256) -> ChainResult { + let id = hex::encode(id); + let payload = mailbox::DeliveredRequest { + message_delivered: mailbox::DeliveredRequestInner { id }, + }; + + let delivered = match self + .provider + .wasm_query(GeneralMailboxQuery { mailbox: payload }, None) + .await + { + Ok(v) => { + let response: mailbox::DeliveredResponse = serde_json::from_slice(&v)?; + + response.delivered + } + Err(err) => { + warn!( + "error while checking the message delivery status: {:?}", + err + ); + + false + } + }; + + Ok(delivered) + } + + #[instrument(err, ret, skip(self))] + async fn default_ism(&self) -> ChainResult { + let payload = mailbox::DefaultIsmRequest { + default_ism: general::EmptyStruct {}, + }; + + let data = self + .provider + .wasm_query(GeneralMailboxQuery { mailbox: payload }, None) + .await?; + let response: mailbox::DefaultIsmResponse = serde_json::from_slice(&data)?; + + // convert Hex to H256 + let ism = H256::from_slice(&hex::decode(response.default_ism)?); + Ok(ism) + } + + #[instrument(err, ret, skip(self))] + async fn recipient_ism(&self, recipient: H256) -> ChainResult { + let address = CosmosAddress::from_h256(recipient, &self.prefix())?.address(); + + let payload = mailbox::RecipientIsmRequest { + recipient_ism: mailbox::RecipientIsmRequestInner { + recipient_addr: address, + }, + }; + + let data = self + .provider + .wasm_query(GeneralMailboxQuery { mailbox: payload }, None) + .await?; + let response: mailbox::RecipientIsmResponse = serde_json::from_slice(&data)?; + + // convert Hex to H256 + let ism = CosmosAddress::from_str(&response.ism)?; + Ok(ism.digest()) + } + + #[instrument(err, ret, skip(self))] + async fn process( + &self, + message: &HyperlaneMessage, + metadata: &[u8], + tx_gas_limit: Option, + ) -> ChainResult { + let process_message = ProcessMessageRequest { + process: ProcessMessageRequestInner { + message: hex::encode(RawHyperlaneMessage::from(message)), + metadata: hex::encode(metadata), + }, + }; + + let response: TxResponse = self + .provider + .wasm_send(process_message, tx_gas_limit) + .await?; + + Ok(tx_response_to_outcome(response)?) + } + + #[instrument(err, ret, skip(self), fields(msg=%message, metadata=%fmt_bytes(metadata)))] + async fn process_estimate_costs( + &self, + message: &HyperlaneMessage, + metadata: &[u8], + ) -> ChainResult { + let process_message = ProcessMessageRequest { + process: ProcessMessageRequestInner { + message: hex::encode(RawHyperlaneMessage::from(message)), + metadata: hex::encode(metadata), + }, + }; + + let gas_limit = self.provider.wasm_estimate_gas(process_message).await?; + + let result = TxCostEstimate { + gas_limit: gas_limit.into(), + gas_price: U256::from(2500), + l2_gas_limit: None, + }; + + Ok(result) + } + + fn process_calldata(&self, message: &HyperlaneMessage, metadata: &[u8]) -> Vec { + todo!() // not required + } +} + +impl CosmosMailbox { + #[instrument(level = "debug", err, ret, skip(self))] + async fn nonce_at_block(&self, block_height: Option) -> ChainResult { + let payload = mailbox::NonceRequest { + nonce: general::EmptyStruct {}, + }; + + let data = self + .provider + .wasm_query(GeneralMailboxQuery { mailbox: payload }, block_height) + .await?; + + let response: mailbox::NonceResponse = serde_json::from_slice(&data)?; + + Ok(response.nonce) + } +} + +// ------------------ Indexer ------------------ + +const MESSAGE_ATTRIBUTE_KEY: &str = "message"; +static MESSAGE_ATTRIBUTE_KEY_BASE64: Lazy = + Lazy::new(|| BASE64.encode(MESSAGE_ATTRIBUTE_KEY)); + +/// Struct that retrieves event data for a Cosmos Mailbox contract +#[derive(Debug)] +pub struct CosmosMailboxIndexer { + mailbox: CosmosMailbox, + indexer: Box, +} + +impl CosmosMailboxIndexer { + /// The message dispatch event type from the CW contract. + const MESSAGE_DISPATCH_EVENT_TYPE: &str = "mailbox_dispatch"; + + /// Create a reference to a mailbox at a specific Cosmos address on some + /// chain + pub fn new( + conf: ConnectionConf, + locator: ContractLocator, + signer: Option, + reorg_period: u32, + ) -> ChainResult { + let mailbox = CosmosMailbox::new(conf.clone(), locator.clone(), signer.clone())?; + let indexer = CosmosWasmIndexer::new( + conf, + locator, + Self::MESSAGE_DISPATCH_EVENT_TYPE.into(), + reorg_period, + )?; + + Ok(Self { + mailbox, + indexer: Box::new(indexer), + }) + } + + fn hyperlane_message_parser( + attrs: &Vec, + ) -> ChainResult> { + let mut contract_address: Option = None; + let mut message: Option = None; + + for attr in attrs { + let key = attr.key.as_str(); + let value = attr.value.as_str(); + + match key { + CONTRACT_ADDRESS_ATTRIBUTE_KEY => { + contract_address = Some(value.to_string()); + } + v if *CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64 == v => { + contract_address = Some(String::from_utf8( + BASE64 + .decode(value) + .map_err(Into::::into)?, + )?); + } + + MESSAGE_ATTRIBUTE_KEY => { + // Intentionally using read_from to get a Result::Err if there's + // an issue with the message. + let mut reader = Cursor::new(hex::decode(value)?); + message = Some(HyperlaneMessage::read_from(&mut reader)?); + } + v if *MESSAGE_ATTRIBUTE_KEY_BASE64 == v => { + // Intentionally using read_from to get a Result::Err if there's + // an issue with the message. + let mut reader = Cursor::new(hex::decode(String::from_utf8( + BASE64 + .decode(value) + .map_err(Into::::into)?, + )?)?); + message = Some(HyperlaneMessage::read_from(&mut reader)?); + } + + _ => {} + } + } + + let contract_address = contract_address + .ok_or_else(|| ChainCommunicationError::from_other_str("missing contract_address"))?; + let message = + message.ok_or_else(|| ChainCommunicationError::from_other_str("missing message"))?; + + Ok(ParsedEvent::new(contract_address, message)) + } +} + +#[async_trait] +impl Indexer for CosmosMailboxIndexer { + async fn fetch_logs( + &self, + range: RangeInclusive, + ) -> ChainResult> { + let result = self + .indexer + .get_range_event_logs(range, Self::hyperlane_message_parser) + .await?; + + Ok(result) + } + + async fn get_finalized_block_number(&self) -> ChainResult { + self.indexer.get_finalized_block_number().await + } +} + +#[async_trait] +impl Indexer for CosmosMailboxIndexer { + async fn fetch_logs(&self, range: RangeInclusive) -> ChainResult> { + // TODO: implement when implementing Cosmos scraping + todo!() + } + + async fn get_finalized_block_number(&self) -> ChainResult { + self.indexer.get_finalized_block_number().await + } +} + +#[async_trait] +impl SequenceIndexer for CosmosMailboxIndexer { + async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { + let tip = Indexer::::get_finalized_block_number(&self).await?; + + // No sequence for message deliveries. + Ok((None, tip)) + } +} + +#[async_trait] +impl SequenceIndexer for CosmosMailboxIndexer { + async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { + let tip = Indexer::::get_finalized_block_number(&self).await?; + + let sequence = self.mailbox.nonce_at_block(Some(tip.into())).await?; + + Ok((Some(sequence), tip)) + } +} + +#[cfg(test)] +mod tests { + use cosmrs::tendermint::abci::EventAttribute; + use hyperlane_core::HyperlaneMessage; + + use crate::{rpc::ParsedEvent, utils::event_attributes_from_str}; + + use super::*; + + #[test] + fn test_hyperlane_message_parser() { + // Examples from https://rpc-kralum.neutron-1.neutron.org/tx_search?query=%22tx.height%20%3E=%204000000%20AND%20tx.height%20%3C=%204100000%20AND%20wasm-mailbox_dispatch._contract_address%20=%20%27neutron1sjzzd4gwkggy6hrrs8kxxatexzcuz3jecsxm3wqgregkulzj8r7qlnuef4%27%22&prove=false&page=1&per_page=100 + + let expected = ParsedEvent::new( + "neutron1sjzzd4gwkggy6hrrs8kxxatexzcuz3jecsxm3wqgregkulzj8r7qlnuef4".into(), + HyperlaneMessage::from(hex::decode("03000000006e74726e0000000000000000000000006ba6343a09a60ac048d0e99f50b76fd99eff1063000000a9000000000000000000000000281973b53c9aacec128ac964a6f750fea40912aa48656c6c6f2066726f6d204e657574726f6e204d61696e6e657420746f204d616e74612050616369666963206f63742032392c2031323a353520616d").unwrap()), + ); + + let assert_parsed_event = |attrs: &Vec| { + let parsed_event = CosmosMailboxIndexer::hyperlane_message_parser(attrs).unwrap(); + + assert_eq!(parsed_event, expected); + }; + + // Non-base64 version + let non_base64_attrs = event_attributes_from_str( + r#"[{"key":"_contract_address","value":"neutron1sjzzd4gwkggy6hrrs8kxxatexzcuz3jecsxm3wqgregkulzj8r7qlnuef4","index":true},{"key":"sender","value":"0000000000000000000000006ba6343a09a60ac048d0e99f50b76fd99eff1063","index":true},{"key":"destination","value":"169","index":true},{"key":"recipient","value":"000000000000000000000000281973b53c9aacec128ac964a6f750fea40912aa","index":true},{"key":"message","value":"03000000006e74726e0000000000000000000000006ba6343a09a60ac048d0e99f50b76fd99eff1063000000a9000000000000000000000000281973b53c9aacec128ac964a6f750fea40912aa48656c6c6f2066726f6d204e657574726f6e204d61696e6e657420746f204d616e74612050616369666963206f63742032392c2031323a353520616d","index":true}]"#, + ); + assert_parsed_event(&non_base64_attrs); + + // Base64 version + let base64_attrs = event_attributes_from_str( + r#"[{"key":"X2NvbnRyYWN0X2FkZHJlc3M=","value":"bmV1dHJvbjFzanp6ZDRnd2tnZ3k2aHJyczhreHhhdGV4emN1ejNqZWNzeG0zd3FncmVna3Vsemo4cjdxbG51ZWY0","index":true},{"key":"c2VuZGVy","value":"MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNmJhNjM0M2EwOWE2MGFjMDQ4ZDBlOTlmNTBiNzZmZDk5ZWZmMTA2Mw==","index":true},{"key":"ZGVzdGluYXRpb24=","value":"MTY5","index":true},{"key":"cmVjaXBpZW50","value":"MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjgxOTczYjUzYzlhYWNlYzEyOGFjOTY0YTZmNzUwZmVhNDA5MTJhYQ==","index":true},{"key":"bWVzc2FnZQ==","value":"MDMwMDAwMDAwMDZlNzQ3MjZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNmJhNjM0M2EwOWE2MGFjMDQ4ZDBlOTlmNTBiNzZmZDk5ZWZmMTA2MzAwMDAwMGE5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjgxOTczYjUzYzlhYWNlYzEyOGFjOTY0YTZmNzUwZmVhNDA5MTJhYTQ4NjU2YzZjNmYyMDY2NzI2ZjZkMjA0ZTY1NzU3NDcyNmY2ZTIwNGQ2MTY5NmU2ZTY1NzQyMDc0NmYyMDRkNjE2ZTc0NjEyMDUwNjE2MzY5NjY2OTYzMjA2ZjYzNzQyMDMyMzkyYzIwMzEzMjNhMzUzNTIwNjE2ZA==","index":true}]"#, + ); + assert_parsed_event(&base64_attrs); + } +} diff --git a/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs b/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs new file mode 100644 index 0000000000..15db14ff7a --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs @@ -0,0 +1,370 @@ +use std::{fmt::Debug, num::NonZeroU64, ops::RangeInclusive, str::FromStr}; + +use async_trait::async_trait; +use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; +use cosmrs::tendermint::abci::EventAttribute; +use hyperlane_core::{ + accumulator::incremental::IncrementalMerkle, ChainCommunicationError, ChainResult, Checkpoint, + ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, + Indexer, LogMeta, MerkleTreeHook, MerkleTreeInsertion, SequenceIndexer, H256, +}; +use once_cell::sync::Lazy; +use tracing::instrument; + +use crate::{ + grpc::{WasmGrpcProvider, WasmProvider}, + payloads::{ + general::{self}, + merkle_tree_hook, + }, + rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer}, + utils::{ + get_block_height_for_lag, CONTRACT_ADDRESS_ATTRIBUTE_KEY, + CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64, + }, + ConnectionConf, CosmosProvider, HyperlaneCosmosError, Signer, +}; + +#[derive(Debug)] +/// A reference to a MerkleTreeHook contract on some Cosmos chain +pub struct CosmosMerkleTreeHook { + /// Domain + domain: HyperlaneDomain, + /// Contract address + address: H256, + /// Provider + provider: Box, +} + +impl CosmosMerkleTreeHook { + /// create new Cosmos MerkleTreeHook agent + pub fn new( + conf: ConnectionConf, + locator: ContractLocator, + signer: Option, + ) -> ChainResult { + let provider = WasmGrpcProvider::new(conf.clone(), locator.clone(), signer)?; + + Ok(Self { + domain: locator.domain.clone(), + address: locator.address, + provider: Box::new(provider), + }) + } +} + +impl HyperlaneContract for CosmosMerkleTreeHook { + fn address(&self) -> H256 { + self.address + } +} + +impl HyperlaneChain for CosmosMerkleTreeHook { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(CosmosProvider::new(self.domain.clone())) + } +} + +#[async_trait] +impl MerkleTreeHook for CosmosMerkleTreeHook { + /// Return the incremental merkle tree in storage + #[instrument(level = "debug", err, ret, skip(self))] + async fn tree(&self, lag: Option) -> ChainResult { + let payload = merkle_tree_hook::MerkleTreeRequest { + tree: general::EmptyStruct {}, + }; + + let block_height = get_block_height_for_lag(&self.provider, lag).await?; + + let data = self + .provider + .wasm_query( + merkle_tree_hook::MerkleTreeGenericRequest { + merkle_hook: payload, + }, + block_height, + ) + .await?; + let response: merkle_tree_hook::MerkleTreeResponse = serde_json::from_slice(&data)?; + + let branch = response + .branch + .iter() + .map(|s| s.as_str()) + .map(H256::from_str) + .collect::, _>>()?; + + let branch_res: [H256; 32] = branch.try_into().map_err(|_| { + ChainCommunicationError::from_other_str("Failed to build merkle branch array") + })?; + + Ok(IncrementalMerkle::new(branch_res, response.count as usize)) + } + + /// Gets the current leaf count of the merkle tree + async fn count(&self, lag: Option) -> ChainResult { + let payload = merkle_tree_hook::MerkleTreeCountRequest { + count: general::EmptyStruct {}, + }; + + let block_height = get_block_height_for_lag(&self.provider, lag).await?; + + self.count_at_block(block_height).await + } + + #[instrument(level = "debug", err, ret, skip(self))] + async fn latest_checkpoint(&self, lag: Option) -> ChainResult { + let payload = merkle_tree_hook::CheckPointRequest { + check_point: general::EmptyStruct {}, + }; + + let block_height = get_block_height_for_lag(&self.provider, lag).await?; + + let data = self + .provider + .wasm_query( + merkle_tree_hook::MerkleTreeGenericRequest { + merkle_hook: payload, + }, + block_height, + ) + .await?; + let response: merkle_tree_hook::CheckPointResponse = serde_json::from_slice(&data)?; + + Ok(Checkpoint { + merkle_tree_hook_address: self.address, + mailbox_domain: self.domain.id(), + root: response.root.parse()?, + index: response.count, + }) + } +} + +impl CosmosMerkleTreeHook { + #[instrument(level = "debug", err, ret, skip(self))] + async fn count_at_block(&self, block_height: Option) -> ChainResult { + let payload = merkle_tree_hook::MerkleTreeCountRequest { + count: general::EmptyStruct {}, + }; + + let data = self + .provider + .wasm_query( + merkle_tree_hook::MerkleTreeGenericRequest { + merkle_hook: payload, + }, + block_height, + ) + .await?; + let response: merkle_tree_hook::MerkleTreeCountResponse = serde_json::from_slice(&data)?; + + Ok(response.count) + } +} + +// ------------------ Indexer ------------------ + +const INDEX_ATTRIBUTE_KEY: &str = "index"; +pub(crate) static INDEX_ATTRIBUTE_KEY_BASE64: Lazy = + Lazy::new(|| BASE64.encode(INDEX_ATTRIBUTE_KEY)); + +const MESSAGE_ID_ATTRIBUTE_KEY: &str = "message_id"; +pub(crate) static MESSAGE_ID_ATTRIBUTE_KEY_BASE64: Lazy = + Lazy::new(|| BASE64.encode(MESSAGE_ID_ATTRIBUTE_KEY)); + +#[derive(Debug)] +/// A reference to a MerkleTreeHookIndexer contract on some Cosmos chain +pub struct CosmosMerkleTreeHookIndexer { + /// The CosmosMerkleTreeHook + merkle_tree_hook: CosmosMerkleTreeHook, + /// Cosmwasm indexer instance + indexer: Box, +} + +impl CosmosMerkleTreeHookIndexer { + /// The message dispatch event type from the CW contract. + const MERKLE_TREE_INSERTION_EVENT_TYPE: &str = "hpl_hook_merkle::post_dispatch"; + + /// create new Cosmos MerkleTreeHookIndexer agent + pub fn new( + conf: ConnectionConf, + locator: ContractLocator, + signer: Option, + reorg_period: u32, + ) -> ChainResult { + let indexer = CosmosWasmIndexer::new( + conf.clone(), + locator.clone(), + Self::MERKLE_TREE_INSERTION_EVENT_TYPE.into(), + reorg_period, + )?; + + Ok(Self { + merkle_tree_hook: CosmosMerkleTreeHook::new(conf, locator, signer)?, + indexer: Box::new(indexer), + }) + } + + fn merkle_tree_insertion_parser( + attrs: &Vec, + ) -> ChainResult> { + let mut contract_address: Option = None; + let mut insertion = IncompleteMerkleTreeInsertion::default(); + + for attr in attrs { + let key = attr.key.as_str(); + let value = attr.value.as_str(); + + match key { + CONTRACT_ADDRESS_ATTRIBUTE_KEY => { + contract_address = Some(value.to_string()); + } + v if *CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64 == v => { + contract_address = Some(String::from_utf8( + BASE64 + .decode(value) + .map_err(Into::::into)?, + )?); + } + + MESSAGE_ID_ATTRIBUTE_KEY => { + insertion.message_id = Some(H256::from_slice(hex::decode(value)?.as_slice())); + } + v if *MESSAGE_ID_ATTRIBUTE_KEY_BASE64 == v => { + insertion.message_id = Some(H256::from_slice( + hex::decode(String::from_utf8( + BASE64 + .decode(value) + .map_err(Into::::into)?, + )?)? + .as_slice(), + )); + } + + INDEX_ATTRIBUTE_KEY => { + insertion.leaf_index = Some(value.parse::()?); + } + v if *INDEX_ATTRIBUTE_KEY_BASE64 == v => { + insertion.leaf_index = Some( + String::from_utf8( + BASE64 + .decode(value) + .map_err(Into::::into)?, + )? + .parse()?, + ); + } + + _ => {} + } + } + + let contract_address = contract_address + .ok_or_else(|| ChainCommunicationError::from_other_str("missing contract_address"))?; + + Ok(ParsedEvent::new(contract_address, insertion.try_into()?)) + } +} + +#[async_trait] +impl Indexer for CosmosMerkleTreeHookIndexer { + /// Fetch list of logs between `range` of blocks + async fn fetch_logs( + &self, + range: RangeInclusive, + ) -> ChainResult> { + let result = self + .indexer + .get_range_event_logs(range, Self::merkle_tree_insertion_parser) + .await?; + + Ok(result) + } + + /// Get the chain's latest block number that has reached finality + async fn get_finalized_block_number(&self) -> ChainResult { + self.indexer.get_finalized_block_number().await + } +} + +#[async_trait] +impl SequenceIndexer for CosmosMerkleTreeHookIndexer { + async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { + let tip = self.get_finalized_block_number().await?; + let sequence = self + .merkle_tree_hook + .count_at_block(Some(tip.into())) + .await?; + + Ok((Some(sequence), tip)) + } +} + +#[derive(Default)] +struct IncompleteMerkleTreeInsertion { + leaf_index: Option, + message_id: Option, +} + +impl TryInto for IncompleteMerkleTreeInsertion { + type Error = ChainCommunicationError; + + fn try_into(self) -> Result { + let leaf_index = self + .leaf_index + .ok_or_else(|| ChainCommunicationError::from_other_str("missing leaf_index"))?; + let message_id = self + .message_id + .ok_or_else(|| ChainCommunicationError::from_other_str("missing message_id"))?; + + Ok(MerkleTreeInsertion::new(leaf_index, message_id)) + } +} + +#[cfg(test)] +mod tests { + use cosmrs::tendermint::abci::EventAttribute; + use hyperlane_core::H256; + use std::str::FromStr; + + use crate::{rpc::ParsedEvent, utils::event_attributes_from_str}; + + use super::*; + + #[test] + fn test_merkle_tree_insertion_parser() { + // Examples from https://rpc-kralum.neutron-1.neutron.org/tx_search?query=%22tx.height%20%3E=%204000000%20AND%20tx.height%20%3C=%204100000%20AND%20wasm-hpl_hook_merkle::post_dispatch._contract_address%20=%20%27neutron1e5c2qqquc86rd3q77aj2wyht40z6z3q5pclaq040ue9f5f8yuf7qnpvkzk%27%22&prove=false&page=1&per_page=100 + + let expected = ParsedEvent::new( + "neutron1e5c2qqquc86rd3q77aj2wyht40z6z3q5pclaq040ue9f5f8yuf7qnpvkzk".into(), + MerkleTreeInsertion::new( + 4, + H256::from_str("a21078beac8bc19770d532eed0b4ada5ef0b45992cde219979f07e3e49185384") + .unwrap(), + ), + ); + + let assert_parsed_event = |attrs: &Vec| { + let parsed_event = + CosmosMerkleTreeHookIndexer::merkle_tree_insertion_parser(attrs).unwrap(); + + assert_eq!(parsed_event, expected); + }; + + // Non-base64 version + let non_base64_attrs = event_attributes_from_str( + r#"[{"key":"_contract_address","value":"neutron1e5c2qqquc86rd3q77aj2wyht40z6z3q5pclaq040ue9f5f8yuf7qnpvkzk","index":true},{"key":"index","value":"4","index":true},{"key":"message_id","value":"a21078beac8bc19770d532eed0b4ada5ef0b45992cde219979f07e3e49185384","index":true}]"#, + ); + assert_parsed_event(&non_base64_attrs); + + // Base64 version + let base64_attrs = event_attributes_from_str( + r#"[{"key":"X2NvbnRyYWN0X2FkZHJlc3M=","value":"bmV1dHJvbjFlNWMycXFxdWM4NnJkM3E3N2FqMnd5aHQ0MHo2ejNxNXBjbGFxMDQwdWU5ZjVmOHl1ZjdxbnB2a3pr","index":true},{"key":"aW5kZXg=","value":"NA==","index":true},{"key":"bWVzc2FnZV9pZA==","value":"YTIxMDc4YmVhYzhiYzE5NzcwZDUzMmVlZDBiNGFkYTVlZjBiNDU5OTJjZGUyMTk5NzlmMDdlM2U0OTE4NTM4NA==","index":true}]"#, + ); + assert_parsed_event(&base64_attrs); + } +} diff --git a/rust/chains/hyperlane-cosmos/src/multisig_ism.rs b/rust/chains/hyperlane-cosmos/src/multisig_ism.rs new file mode 100644 index 0000000000..a9d84dec7f --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/multisig_ism.rs @@ -0,0 +1,85 @@ +use std::str::FromStr; + +use crate::{ + grpc::{WasmGrpcProvider, WasmProvider}, + payloads::ism_routes::QueryIsmGeneralRequest, + signers::Signer, + ConnectionConf, CosmosProvider, +}; +use async_trait::async_trait; +use hyperlane_core::{ + ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + HyperlaneMessage, HyperlaneProvider, MultisigIsm, RawHyperlaneMessage, H160, H256, +}; + +use crate::payloads::multisig_ism::{self, VerifyInfoRequest, VerifyInfoRequestInner}; + +/// A reference to a MultisigIsm contract on some Cosmos chain +#[derive(Debug)] +pub struct CosmosMultisigIsm { + domain: HyperlaneDomain, + address: H256, + provider: Box, +} + +impl CosmosMultisigIsm { + /// create a new instance of CosmosMultisigIsm + pub fn new( + conf: ConnectionConf, + locator: ContractLocator, + signer: Option, + ) -> ChainResult { + let provider = WasmGrpcProvider::new(conf.clone(), locator.clone(), signer)?; + + Ok(Self { + domain: locator.domain.clone(), + address: locator.address, + provider: Box::new(provider), + }) + } +} + +impl HyperlaneContract for CosmosMultisigIsm { + fn address(&self) -> H256 { + self.address + } +} + +impl HyperlaneChain for CosmosMultisigIsm { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(CosmosProvider::new(self.domain.clone())) + } +} + +#[async_trait] +impl MultisigIsm for CosmosMultisigIsm { + /// Returns the validator and threshold needed to verify message + async fn validators_and_threshold( + &self, + message: &HyperlaneMessage, + ) -> ChainResult<(Vec, u8)> { + let payload = VerifyInfoRequest { + verify_info: VerifyInfoRequestInner { + message: hex::encode(RawHyperlaneMessage::from(message)), + }, + }; + + let data = self + .provider + .wasm_query(QueryIsmGeneralRequest { ism: payload }, None) + .await?; + let response: multisig_ism::VerifyInfoResponse = serde_json::from_slice(&data)?; + + let validators: ChainResult> = response + .validators + .iter() + .map(|v| H160::from_str(v).map(H256::from).map_err(Into::into)) + .collect(); + + Ok((validators?, response.threshold)) + } +} diff --git a/rust/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs b/rust/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs new file mode 100644 index 0000000000..7bb5d40d09 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs @@ -0,0 +1,30 @@ +use hyperlane_core::{HyperlaneMessage, RawHyperlaneMessage}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct ModulesAndThresholdRequest { + modules_and_threshold: ModulesAndThresholdRequestInner, +} + +impl ModulesAndThresholdRequest { + pub fn new(message: &HyperlaneMessage) -> Self { + Self { + modules_and_threshold: ModulesAndThresholdRequestInner { + message: hex::encode(RawHyperlaneMessage::from(message)), + }, + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +struct ModulesAndThresholdRequestInner { + /// Hex-encoded Hyperlane message + pub message: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ModulesAndThresholdResponse { + pub threshold: u8, + /// Bech32-encoded module addresses + pub modules: Vec, +} diff --git a/rust/chains/hyperlane-cosmos/src/payloads/general.rs b/rust/chains/hyperlane-cosmos/src/payloads/general.rs new file mode 100644 index 0000000000..488cae2d37 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/payloads/general.rs @@ -0,0 +1,34 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct EmptyStruct {} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Log { + pub msg_index: u64, + pub events: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Event { + #[serde(rename = "type")] + pub typ: String, + pub attributes: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct EventAttribute { + pub key: String, + pub value: String, + pub index: bool, +} + +impl From for cosmrs::tendermint::abci::EventAttribute { + fn from(val: EventAttribute) -> Self { + cosmrs::tendermint::abci::EventAttribute { + key: val.key, + value: val.value, + index: val.index, + } + } +} diff --git a/rust/chains/hyperlane-cosmos/src/payloads/ism_routes.rs b/rust/chains/hyperlane-cosmos/src/payloads/ism_routes.rs new file mode 100644 index 0000000000..052a1cc48b --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/payloads/ism_routes.rs @@ -0,0 +1,43 @@ +use super::general::EmptyStruct; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct IsmRouteRequest { + pub route: IsmRouteRequestInner, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct IsmRouteRequestInner { + pub message: String, // hexbinary +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct IsmRouteRespnose { + pub ism: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct QueryRoutingIsmGeneralRequest { + pub routing_ism: T, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct QueryRoutingIsmRouteResponse { + pub ism: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct QueryIsmGeneralRequest { + pub ism: T, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct QueryIsmModuleTypeRequest { + pub module_type: EmptyStruct, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct QueryIsmModuleTypeResponse { + #[serde(rename = "type")] + pub typ: hpl_interface::ism::IsmType, +} diff --git a/rust/chains/hyperlane-cosmos/src/payloads/mailbox.rs b/rust/chains/hyperlane-cosmos/src/payloads/mailbox.rs new file mode 100644 index 0000000000..145ba5b16c --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/payloads/mailbox.rs @@ -0,0 +1,81 @@ +use serde::{Deserialize, Serialize}; + +use super::general::EmptyStruct; + +// Requests +#[derive(Serialize, Deserialize, Debug)] +pub struct GeneralMailboxQuery { + pub mailbox: T, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct CountRequest { + pub count: EmptyStruct, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct NonceRequest { + pub nonce: EmptyStruct, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct RecipientIsmRequest { + pub recipient_ism: RecipientIsmRequestInner, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct RecipientIsmRequestInner { + pub recipient_addr: String, // hexbinary +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct DefaultIsmRequest { + pub default_ism: EmptyStruct, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct DeliveredRequest { + pub message_delivered: DeliveredRequestInner, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct DeliveredRequestInner { + pub id: String, // hexbinary +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ProcessMessageRequest { + pub process: ProcessMessageRequestInner, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ProcessMessageRequestInner { + pub metadata: String, + pub message: String, +} + +// Responses +#[derive(Serialize, Deserialize, Debug)] +pub struct CountResponse { + pub count: u32, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct NonceResponse { + pub nonce: u32, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct DefaultIsmResponse { + pub default_ism: String, // hexbineary +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct DeliveredResponse { + pub delivered: bool, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct RecipientIsmResponse { + pub ism: String, +} diff --git a/rust/chains/hyperlane-cosmos/src/payloads/merkle_tree_hook.rs b/rust/chains/hyperlane-cosmos/src/payloads/merkle_tree_hook.rs new file mode 100644 index 0000000000..7635f0ef72 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/payloads/merkle_tree_hook.rs @@ -0,0 +1,46 @@ +use serde::{Deserialize, Serialize}; + +use super::general::EmptyStruct; + +const TREE_DEPTH: usize = 32; + +#[derive(Serialize, Deserialize, Debug)] +pub struct MerkleTreeGenericRequest { + pub merkle_hook: T, +} + +// --------- Requests --------- + +#[derive(Serialize, Deserialize, Debug)] +pub struct MerkleTreeRequest { + pub tree: EmptyStruct, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct MerkleTreeCountRequest { + pub count: EmptyStruct, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct CheckPointRequest { + pub check_point: EmptyStruct, +} + +// --------- Responses --------- + +#[derive(Serialize, Deserialize, Debug)] +pub struct MerkleTreeResponse { + pub branch: [String; TREE_DEPTH], + pub count: u32, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct MerkleTreeCountResponse { + pub count: u32, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct CheckPointResponse { + pub root: String, + pub count: u32, +} diff --git a/rust/chains/hyperlane-cosmos/src/payloads/mod.rs b/rust/chains/hyperlane-cosmos/src/payloads/mod.rs new file mode 100644 index 0000000000..980e501a3c --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/payloads/mod.rs @@ -0,0 +1,7 @@ +pub mod aggregate_ism; +pub mod general; +pub mod ism_routes; +pub mod mailbox; +pub mod merkle_tree_hook; +pub mod multisig_ism; +pub mod validator_announce; diff --git a/rust/chains/hyperlane-cosmos/src/payloads/multisig_ism.rs b/rust/chains/hyperlane-cosmos/src/payloads/multisig_ism.rs new file mode 100644 index 0000000000..204e726dc7 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/payloads/multisig_ism.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct VerifyInfoRequest { + pub verify_info: VerifyInfoRequestInner, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct VerifyInfoRequestInner { + pub message: String, // hexbinary +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct VerifyInfoResponse { + pub threshold: u8, + pub validators: Vec, +} diff --git a/rust/chains/hyperlane-cosmos/src/payloads/validator_announce.rs b/rust/chains/hyperlane-cosmos/src/payloads/validator_announce.rs new file mode 100644 index 0000000000..fdf449c7c4 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/payloads/validator_announce.rs @@ -0,0 +1,42 @@ +use serde::{Deserialize, Serialize}; + +use super::general::EmptyStruct; + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetAnnouncedValidatorsRequest { + pub get_announced_validators: EmptyStruct, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetAnnounceStorageLocationsRequest { + pub get_announce_storage_locations: GetAnnounceStorageLocationsRequestInner, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetAnnounceStorageLocationsRequestInner { + pub validators: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AnnouncementRequest { + pub announce: AnnouncementRequestInner, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AnnouncementRequestInner { + pub validator: String, + pub storage_location: String, + pub signature: String, +} + +// ========= resp ============ + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetAnnouncedValidatorsResponse { + pub validators: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetAnnounceStorageLocationsResponse { + pub storage_locations: Vec<(String, Vec)>, +} diff --git a/rust/chains/hyperlane-cosmos/src/providers/grpc.rs b/rust/chains/hyperlane-cosmos/src/providers/grpc.rs new file mode 100644 index 0000000000..a47d660ded --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/providers/grpc.rs @@ -0,0 +1,372 @@ +use async_trait::async_trait; +use cosmrs::{ + proto::{ + cosmos::{ + auth::v1beta1::{ + query_client::QueryClient as QueryAccountClient, BaseAccount, QueryAccountRequest, + }, + base::{ + abci::v1beta1::TxResponse, + tendermint::v1beta1::{service_client::ServiceClient, GetLatestBlockRequest}, + }, + tx::v1beta1::{ + service_client::ServiceClient as TxServiceClient, BroadcastMode, + BroadcastTxRequest, SimulateRequest, TxRaw, + }, + }, + cosmwasm::wasm::v1::{ + query_client::QueryClient as WasmQueryClient, MsgExecuteContract, + QuerySmartContractStateRequest, + }, + traits::Message, + }, + tx::{self, Fee, MessageExt, SignDoc, SignerInfo}, + Amount, Coin, +}; +use hyperlane_core::{ChainCommunicationError, ChainResult, ContractLocator, U256}; +use serde::Serialize; +use tonic::transport::{Channel, Endpoint}; + +use crate::address::CosmosAddress; +use crate::HyperlaneCosmosError; +use crate::{signers::Signer, ConnectionConf}; + +/// The gas price to use for transactions. +/// TODO: is there a nice way to get a suggested price dynamically? +const DEFAULT_GAS_PRICE: f64 = 0.05; +/// A multiplier applied to a simulated transaction's gas usage to +/// calculate the estimated gas. +const GAS_ESTIMATE_MULTIPLIER: f64 = 1.25; +/// The number of blocks in the future in which a transaction will +/// be valid for. +const TIMEOUT_BLOCKS: u64 = 1000; + +#[async_trait] +/// Cosmwasm GRPC Provider +pub trait WasmProvider: Send + Sync { + /// Get latest block height. + /// Note that in Tendermint, validators come to consensus on a block + /// before they execute the transactions in that block. This means that + /// we may not be able to make state queries against this block until + /// the next one is committed! + async fn latest_block_height(&self) -> ChainResult; + + /// Perform a wasm query against the stored contract address. + async fn wasm_query( + &self, + payload: T, + block_height: Option, + ) -> ChainResult>; + + /// Perform a wasm query against a specified contract address. + async fn wasm_query_to( + &self, + to: String, + payload: T, + block_height: Option, + ) -> ChainResult>; + + /// Send a wasm tx. + async fn wasm_send( + &self, + payload: T, + gas_limit: Option, + ) -> ChainResult; + + /// Estimate gas for a wasm tx. + async fn wasm_estimate_gas(&self, payload: T) -> ChainResult; +} + +#[derive(Debug)] +/// CosmWasm GRPC provider. +pub struct WasmGrpcProvider { + /// Connection configuration. + conf: ConnectionConf, + /// A contract address that can be used as the default + /// for queries / sends / estimates. + contract_address: CosmosAddress, + /// Signer for transactions. + signer: Option, + /// GRPC Channel that can be cheaply cloned. + /// See https://docs.rs/tonic/latest/tonic/transport/struct.Channel.html#multiplexing-requests + channel: Channel, +} + +impl WasmGrpcProvider { + /// Create new CosmWasm GRPC Provider. + pub fn new( + conf: ConnectionConf, + locator: ContractLocator, + signer: Option, + ) -> ChainResult { + let endpoint = + Endpoint::new(conf.get_grpc_url()).map_err(Into::::into)?; + let channel = endpoint.connect_lazy(); + let contract_address = CosmosAddress::from_h256(locator.address, &conf.get_prefix())?; + + Ok(Self { + conf, + contract_address, + signer, + channel, + }) + } + + /// Gets a signer, or returns an error if one is not available. + fn get_signer(&self) -> ChainResult<&Signer> { + self.signer + .as_ref() + .ok_or(ChainCommunicationError::SignerUnavailable) + } +} + +impl WasmGrpcProvider { + /// Generates an unsigned SignDoc for a transaction. + async fn generate_unsigned_sign_doc( + &self, + msgs: Vec, + gas_limit: u64, + ) -> ChainResult { + // As this function is only used for estimating gas or sending transactions, + // we can reasonably expect to have a signer. + let signer = self.get_signer()?; + let account_info = self.account_query(signer.address.clone()).await?; + let current_height = self.latest_block_height().await?; + let timeout_height = current_height + TIMEOUT_BLOCKS; + + let tx_body = tx::Body::new( + msgs, + String::default(), + TryInto::::try_into(timeout_height) + .map_err(ChainCommunicationError::from_other)?, + ); + let signer_info = SignerInfo::single_direct(Some(signer.public_key), account_info.sequence); + + let auth_info = signer_info.auth_info(Fee::from_amount_and_gas( + Coin::new( + Amount::from((gas_limit as f64 * DEFAULT_GAS_PRICE) as u64), + self.conf.get_canonical_asset().as_str(), + ) + .map_err(Into::::into)?, + gas_limit, + )); + + let chain_id = self + .conf + .get_chain_id() + .parse() + .map_err(Into::::into)?; + + Ok( + SignDoc::new(&tx_body, &auth_info, &chain_id, account_info.account_number) + .map_err(Into::::into)?, + ) + } + + /// Generates a raw signed transaction including `msgs`, estimating gas if a limit is not provided. + async fn generate_raw_signed_tx( + &self, + msgs: Vec, + gas_limit: Option, + ) -> ChainResult> { + let gas_limit = if let Some(l) = gas_limit { + l + } else { + self.estimate_gas(msgs.clone()).await? + }; + + let sign_doc = self.generate_unsigned_sign_doc(msgs, gas_limit).await?; + + let signer = self.get_signer()?; + let tx_signed = sign_doc + .sign(&signer.signing_key()?) + .map_err(Into::::into)?; + Ok(tx_signed + .to_bytes() + .map_err(Into::::into)?) + } + + /// Estimates gas for a transaction containing `msgs`. + async fn estimate_gas(&self, msgs: Vec) -> ChainResult { + // Get a sign doc with 0 gas, because we plan to simulate + let sign_doc = self.generate_unsigned_sign_doc(msgs, 0).await?; + + let raw_tx = TxRaw { + body_bytes: sign_doc.body_bytes, + auth_info_bytes: sign_doc.auth_info_bytes, + // The poorly documented trick to simuluating a tx without a valid signature is to just pass + // in a single empty signature. Taken from cosmjs: + // https://github.com/cosmos/cosmjs/blob/44893af824f0712d1f406a8daa9fcae335422235/packages/stargate/src/modules/tx/queries.ts#L67 + signatures: vec![vec![]], + }; + + let mut client = TxServiceClient::new(self.channel.clone()); + let tx_bytes = raw_tx + .to_bytes() + .map_err(ChainCommunicationError::from_other)?; + #[allow(deprecated)] + let sim_req = tonic::Request::new(SimulateRequest { tx: None, tx_bytes }); + let gas_used = client + .simulate(sim_req) + .await + .map_err(ChainCommunicationError::from_other)? + .into_inner() + .gas_info + .ok_or_else(|| ChainCommunicationError::from_other_str("gas info not present"))? + .gas_used; + + let gas_estimate = (gas_used as f64 * GAS_ESTIMATE_MULTIPLIER) as u64; + + Ok(gas_estimate) + } + + /// Queries an account. + async fn account_query(&self, account: String) -> ChainResult { + let mut client = QueryAccountClient::new(self.channel.clone()); + + let request = tonic::Request::new(QueryAccountRequest { address: account }); + let response = client + .account(request) + .await + .map_err(ChainCommunicationError::from_other)? + .into_inner(); + + let account = BaseAccount::decode( + response + .account + .ok_or_else(|| ChainCommunicationError::from_other_str("account not present"))? + .value + .as_slice(), + ) + .map_err(Into::::into)?; + Ok(account) + } +} + +#[async_trait] +impl WasmProvider for WasmGrpcProvider { + async fn latest_block_height(&self) -> ChainResult { + let mut client = ServiceClient::new(self.channel.clone()); + let request = tonic::Request::new(GetLatestBlockRequest {}); + + let response = client + .get_latest_block(request) + .await + .map_err(ChainCommunicationError::from_other)? + .into_inner(); + let height = response + .block + .ok_or_else(|| ChainCommunicationError::from_other_str("block not present"))? + .header + .ok_or_else(|| ChainCommunicationError::from_other_str("header not present"))? + .height; + + Ok(height as u64) + } + + async fn wasm_query(&self, payload: T, block_height: Option) -> ChainResult> + where + T: Serialize + Send + Sync, + { + self.wasm_query_to(self.contract_address.address(), payload, block_height) + .await + } + + async fn wasm_query_to( + &self, + to: String, + payload: T, + block_height: Option, + ) -> ChainResult> + where + T: Serialize + Send + Sync, + { + let mut client = WasmQueryClient::new(self.channel.clone()); + let mut request = tonic::Request::new(QuerySmartContractStateRequest { + address: to, + query_data: serde_json::to_string(&payload)?.as_bytes().to_vec(), + }); + + if let Some(block_height) = block_height { + request + .metadata_mut() + .insert("x-cosmos-block-height", block_height.into()); + } + + let response = client + .smart_contract_state(request) + .await + .map_err(ChainCommunicationError::from_other)? + .into_inner(); + + Ok(response.data) + } + + async fn wasm_send(&self, payload: T, gas_limit: Option) -> ChainResult + where + T: Serialize + Send + Sync, + { + let signer = self.get_signer()?; + let mut client = TxServiceClient::new(self.channel.clone()); + + let msgs = vec![MsgExecuteContract { + sender: signer.address.clone(), + contract: self.contract_address.address(), + msg: serde_json::to_string(&payload)?.as_bytes().to_vec(), + funds: vec![], + } + .to_any() + .map_err(ChainCommunicationError::from_other)?]; + + // We often use U256s to represent gas limits, but Cosmos expects u64s. Try to convert, + // and if it fails, just fallback to None which will result in gas estimation. + let gas_limit: Option = gas_limit.and_then(|limit| match limit.try_into() { + Ok(limit) => Some(limit), + Err(err) => { + tracing::warn!( + ?err, + "failed to convert gas_limit to u64, falling back to estimation" + ); + None + } + }); + + let tx_req = BroadcastTxRequest { + tx_bytes: self.generate_raw_signed_tx(msgs, gas_limit).await?, + mode: BroadcastMode::Sync as i32, + }; + + let tx_res = client + .broadcast_tx(tx_req) + .await + .map_err(Into::::into)? + .into_inner() + .tx_response + .ok_or_else(|| ChainCommunicationError::from_other_str("Empty tx_response"))?; + + Ok(tx_res) + } + + async fn wasm_estimate_gas(&self, payload: T) -> ChainResult + where + T: Serialize + Send + Sync, + { + // Estimating gas requires a signer, which we can reasonably expect to have + // since we need one to send a tx with the estimated gas anyways. + let signer = self.get_signer()?; + let msg = MsgExecuteContract { + sender: signer.address.clone(), + contract: self.contract_address.address(), + msg: serde_json::to_string(&payload)?.as_bytes().to_vec(), + funds: vec![], + }; + + let response = self + .estimate_gas(vec![msg + .to_any() + .map_err(ChainCommunicationError::from_other)?]) + .await?; + + Ok(response) + } +} diff --git a/rust/chains/hyperlane-cosmos/src/providers/mod.rs b/rust/chains/hyperlane-cosmos/src/providers/mod.rs new file mode 100644 index 0000000000..cf9422b2f8 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/providers/mod.rs @@ -0,0 +1,50 @@ +use async_trait::async_trait; +use hyperlane_core::{ + BlockInfo, ChainResult, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, TxnInfo, H256, +}; + +/// cosmos grpc provider +pub mod grpc; +/// cosmos rpc provider +pub mod rpc; + +/// A reference to a Cosmos chain +#[derive(Debug)] +pub struct CosmosProvider { + domain: HyperlaneDomain, +} + +impl CosmosProvider { + /// Create a reference to a Cosmos chain + pub fn new(domain: HyperlaneDomain) -> Self { + Self { domain } + } +} + +impl HyperlaneChain for CosmosProvider { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(CosmosProvider { + domain: self.domain.clone(), + }) + } +} + +#[async_trait] +impl HyperlaneProvider for CosmosProvider { + async fn get_block_by_hash(&self, _hash: &H256) -> ChainResult { + todo!() // FIXME + } + + async fn get_txn_by_hash(&self, _hash: &H256) -> ChainResult { + todo!() // FIXME + } + + async fn is_contract(&self, _address: &H256) -> ChainResult { + // FIXME + Ok(true) + } +} diff --git a/rust/chains/hyperlane-cosmos/src/providers/rpc.rs b/rust/chains/hyperlane-cosmos/src/providers/rpc.rs new file mode 100644 index 0000000000..88c5ded065 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/providers/rpc.rs @@ -0,0 +1,236 @@ +use std::ops::RangeInclusive; + +use async_trait::async_trait; +use cosmrs::rpc::client::{Client, CompatMode, HttpClient}; +use cosmrs::rpc::endpoint::{tx, tx_search::Response as TxSearchResponse}; +use cosmrs::rpc::query::Query; +use cosmrs::rpc::Order; +use cosmrs::tendermint::abci::EventAttribute; +use hyperlane_core::{ChainCommunicationError, ChainResult, ContractLocator, LogMeta, H256, U256}; +use tracing::{instrument, trace}; + +use crate::address::CosmosAddress; +use crate::{ConnectionConf, HyperlaneCosmosError}; + +const PAGINATION_LIMIT: u8 = 100; + +#[async_trait] +/// Trait for wasm indexer. Use rpc provider +pub trait WasmIndexer: Send + Sync { + /// Get the finalized block height. + async fn get_finalized_block_number(&self) -> ChainResult; + + /// Get logs for the given range using the given parser. + async fn get_range_event_logs( + &self, + range: RangeInclusive, + parser: for<'a> fn(&'a Vec) -> ChainResult>, + ) -> ChainResult> + where + T: Send + Sync + PartialEq + 'static; +} + +#[derive(Debug, Eq, PartialEq)] +/// An event parsed from the RPC response. +pub struct ParsedEvent { + contract_address: String, + event: T, +} + +impl ParsedEvent { + /// Create a new ParsedEvent. + pub fn new(contract_address: String, event: T) -> Self { + Self { + contract_address, + event, + } + } +} + +#[derive(Debug)] +/// Cosmwasm RPC Provider +pub struct CosmosWasmIndexer { + client: HttpClient, + contract_address: CosmosAddress, + target_event_kind: String, + reorg_period: u32, +} + +impl CosmosWasmIndexer { + const WASM_TYPE: &str = "wasm"; + + /// create new Cosmwasm RPC Provider + pub fn new( + conf: ConnectionConf, + locator: ContractLocator, + event_type: String, + reorg_period: u32, + ) -> ChainResult { + let client = HttpClient::builder( + conf.get_rpc_url() + .parse() + .map_err(Into::::into)?, + ) + // Consider supporting different compatibility modes. + .compat_mode(CompatMode::latest()) + .build() + .map_err(Into::::into)?; + Ok(Self { + client, + contract_address: CosmosAddress::from_h256( + locator.address, + conf.get_prefix().as_str(), + )?, + target_event_kind: format!("{}-{}", Self::WASM_TYPE, event_type), + reorg_period, + }) + } +} + +impl CosmosWasmIndexer { + #[instrument(level = "trace", err, skip(self))] + async fn tx_search(&self, query: Query, page: u32) -> ChainResult { + Ok(self + .client + .tx_search(query, false, page, PAGINATION_LIMIT, Order::Ascending) + .await + .map_err(Into::::into)?) + } + + // Iterate through all txs, filter out failed txs, find target events + // in successful txs, and parse them. + fn handle_txs( + &self, + txs: Vec, + parser: for<'a> fn(&'a Vec) -> ChainResult>, + ) -> ChainResult + '_> + where + T: PartialEq + 'static, + { + let logs_iter = txs + .into_iter() + .filter(|tx| { + // Filter out failed txs + let tx_failed = tx.tx_result.code.is_err(); + if tx_failed { + trace!(tx_hash=?tx.hash, "Indexed tx has failed, skipping"); + } + !tx_failed + }) + .flat_map(move |tx| { + // Find target events in successful txs + self.handle_tx(tx, parser) + }); + + Ok(logs_iter) + } + + // Iter through all events in the tx, looking for any target events + // made by the contract we are indexing. + fn handle_tx( + &self, + tx: tx::Response, + parser: for<'a> fn(&'a Vec) -> ChainResult>, + ) -> impl Iterator + '_ + where + T: PartialEq + 'static, + { + tx.tx_result.events.into_iter().enumerate().filter_map(move |(log_idx, event)| { + if event.kind.as_str() != self.target_event_kind { + return None; + } + + parser(&event.attributes) + .map_err(|err| { + // This can happen if we attempt to parse an event that just happens + // to have the same name but a different structure. + tracing::trace!(?err, tx_hash=?tx.hash, log_idx, ?event, "Failed to parse event attributes"); + }) + .ok() + .and_then(|parsed_event| { + // This is crucial! We need to make sure that the contract address + // in the event matches the contract address we are indexing. + // Otherwise, we might index events from other contracts that happen + // to have the same target event name. + if parsed_event.contract_address != self.contract_address.address() { + trace!(tx_hash=?tx.hash, log_idx, ?event, "Event contract address does not match indexer contract address"); + return None; + } + + Some((parsed_event.event, LogMeta { + address: self.contract_address.digest(), + block_number: tx.height.value(), + // FIXME: block_hash is not available in tx_search. + // This isn't strictly required atm. + block_hash: H256::zero(), + transaction_id: H256::from_slice(tx.hash.as_bytes()).into(), + transaction_index: tx.index.into(), + log_index: U256::from(log_idx), + })) + }) + }) + } +} + +#[async_trait] +impl WasmIndexer for CosmosWasmIndexer { + async fn get_finalized_block_number(&self) -> ChainResult { + let latest_height: u32 = self + .client + .latest_block() + .await + .map_err(Into::::into)? + .block + .header + .height + .value() + .try_into() + .map_err(ChainCommunicationError::from_other)?; + Ok(latest_height.saturating_sub(self.reorg_period)) + } + + #[instrument(err, skip(self, parser))] + async fn get_range_event_logs( + &self, + range: RangeInclusive, + parser: for<'a> fn(&'a Vec) -> ChainResult>, + ) -> ChainResult> + where + T: PartialEq + Send + Sync + 'static, + { + // Page starts from 1 + let query = Query::default() + .and_gte("tx.height", *range.start() as u64) + .and_lte("tx.height", *range.end() as u64) + .and_eq( + format!("{}._contract_address", self.target_event_kind), + self.contract_address.address(), + ); + + let tx_search_result = self.tx_search(query.clone(), 1).await?; + + // Using the first tx_search_result, we can calculate the total number of pages. + let total_count = tx_search_result.total_count; + let last_page = div_ceil(total_count, PAGINATION_LIMIT.into()); + + let mut logs = self + .handle_txs(tx_search_result.txs, parser)? + .collect::>(); + + // If there are any more pages, fetch them and append to the result. + for page in 2..=last_page { + trace!(page, "Performing tx search"); + + let tx_search_result = self.tx_search(query.clone(), page).await?; + + logs.extend(self.handle_txs(tx_search_result.txs, parser)?); + } + + Ok(logs) + } +} + +// TODO: just use div_ceil when upgrading from 1.72.1 to 1.73.0 or above +fn div_ceil(numerator: u32, denominator: u32) -> u32 { + (numerator as f32 / denominator as f32).ceil() as u32 +} diff --git a/rust/chains/hyperlane-cosmos/src/routing_ism.rs b/rust/chains/hyperlane-cosmos/src/routing_ism.rs new file mode 100644 index 0000000000..0a646c005b --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/routing_ism.rs @@ -0,0 +1,83 @@ +use std::str::FromStr; + +use async_trait::async_trait; + +use hyperlane_core::{ + ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + HyperlaneMessage, HyperlaneProvider, RawHyperlaneMessage, RoutingIsm, H256, +}; + +use crate::{ + address::CosmosAddress, + grpc::{WasmGrpcProvider, WasmProvider}, + payloads::ism_routes::{ + IsmRouteRequest, IsmRouteRequestInner, IsmRouteRespnose, QueryRoutingIsmGeneralRequest, + }, + signers::Signer, + ConnectionConf, CosmosProvider, +}; + +/// A reference to a RoutingIsm contract on some Cosmos chain +#[derive(Debug)] +pub struct CosmosRoutingIsm { + domain: HyperlaneDomain, + address: H256, + provider: Box, +} + +impl CosmosRoutingIsm { + /// create a new instance of CosmosRoutingIsm + pub fn new( + conf: &ConnectionConf, + locator: ContractLocator, + signer: Option, + ) -> ChainResult { + let provider = WasmGrpcProvider::new(conf.clone(), locator.clone(), signer)?; + + Ok(Self { + domain: locator.domain.clone(), + address: locator.address, + provider: Box::new(provider), + }) + } +} + +impl HyperlaneContract for CosmosRoutingIsm { + fn address(&self) -> H256 { + self.address + } +} + +impl HyperlaneChain for CosmosRoutingIsm { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(CosmosProvider::new(self.domain.clone())) + } +} + +#[async_trait] +impl RoutingIsm for CosmosRoutingIsm { + async fn route(&self, message: &HyperlaneMessage) -> ChainResult { + let payload = IsmRouteRequest { + route: IsmRouteRequestInner { + message: hex::encode(RawHyperlaneMessage::from(message)), + }, + }; + + let data = self + .provider + .wasm_query( + QueryRoutingIsmGeneralRequest { + routing_ism: payload, + }, + None, + ) + .await?; + let response: IsmRouteRespnose = serde_json::from_slice(&data)?; + + Ok(CosmosAddress::from_str(&response.ism)?.digest()) + } +} diff --git a/rust/chains/hyperlane-cosmos/src/signers.rs b/rust/chains/hyperlane-cosmos/src/signers.rs new file mode 100644 index 0000000000..60870fc922 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/signers.rs @@ -0,0 +1,47 @@ +use cosmrs::crypto::{secp256k1::SigningKey, PublicKey}; +use hyperlane_core::ChainResult; + +use crate::{address::CosmosAddress, HyperlaneCosmosError}; + +#[derive(Clone, Debug)] +/// Signer for cosmos chain +pub struct Signer { + /// public key + pub public_key: PublicKey, + /// precomputed address, because computing it is a fallible operation + /// and we want to avoid returning `Result` + pub address: String, + /// address prefix + pub prefix: String, + private_key: Vec, +} + +impl Signer { + /// create new signer + /// + /// # Arguments + /// * `private_key` - private key for signer + /// * `prefix` - prefix for signer address + pub fn new(private_key: Vec, prefix: String) -> ChainResult { + let address = CosmosAddress::from_privkey(&private_key, &prefix)?.address(); + let signing_key = Self::build_signing_key(&private_key)?; + let public_key = signing_key.public_key(); + Ok(Self { + public_key, + private_key, + address, + prefix, + }) + } + + /// Build a SigningKey from a private key. This cannot be + /// precompiled and stored in `Signer`, because `SigningKey` is not `Sync`. + pub fn signing_key(&self) -> ChainResult { + Self::build_signing_key(&self.private_key) + } + + fn build_signing_key(private_key: &Vec) -> ChainResult { + Ok(SigningKey::from_slice(private_key.as_slice()) + .map_err(Into::::into)?) + } +} diff --git a/rust/chains/hyperlane-cosmos/src/trait_builder.rs b/rust/chains/hyperlane-cosmos/src/trait_builder.rs new file mode 100644 index 0000000000..8629970bd9 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/trait_builder.rs @@ -0,0 +1,78 @@ +/// Cosmos connection configuration +#[derive(Debug, Clone)] +pub struct ConnectionConf { + /// The GRPC url to connect to + grpc_url: String, + /// The RPC url to connect to + rpc_url: String, + /// The chain ID + chain_id: String, + /// The prefix for the account address + prefix: String, + /// Canoncial Assets Denom + canonical_asset: String, +} + +/// An error type when parsing a connection configuration. +#[derive(thiserror::Error, Debug)] +pub enum ConnectionConfError { + /// Missing `rpc_url` for connection configuration + #[error("Missing `rpc_url` for connection configuration")] + MissingConnectionRpcUrl, + /// Missing `grpc_url` for connection configuration + #[error("Missing `grpc_url` for connection configuration")] + MissingConnectionGrpcUrl, + /// Missing `chainId` for connection configuration + #[error("Missing `chainId` for connection configuration")] + MissingChainId, + /// Missing `prefix` for connection configuration + #[error("Missing `prefix` for connection configuration")] + MissingPrefix, + /// Invalid `url` for connection configuration + #[error("Invalid `url` for connection configuration: `{0}` ({1})")] + InvalidConnectionUrl(String, url::ParseError), +} + +impl ConnectionConf { + /// Get the GRPC url + pub fn get_grpc_url(&self) -> String { + self.grpc_url.clone() + } + + /// Get the RPC url + pub fn get_rpc_url(&self) -> String { + self.rpc_url.clone() + } + + /// Get the chain ID + pub fn get_chain_id(&self) -> String { + self.chain_id.clone() + } + + /// Get the prefix + pub fn get_prefix(&self) -> String { + self.prefix.clone() + } + + /// Get the asset + pub fn get_canonical_asset(&self) -> String { + self.canonical_asset.clone() + } + + /// Create a new connection configuration + pub fn new( + grpc_url: String, + rpc_url: String, + chain_id: String, + prefix: String, + canonical_asset: String, + ) -> Self { + Self { + grpc_url, + rpc_url, + chain_id, + prefix, + canonical_asset, + } + } +} diff --git a/rust/chains/hyperlane-cosmos/src/types.rs b/rust/chains/hyperlane-cosmos/src/types.rs new file mode 100644 index 0000000000..264ae8791e --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/types.rs @@ -0,0 +1,34 @@ +use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; +use hyperlane_core::{ChainResult, ModuleType, TxOutcome, H256, U256}; + +pub struct IsmType(pub hpl_interface::ism::IsmType); + +impl From for IsmType { + fn from(value: hpl_interface::ism::IsmType) -> Self { + IsmType(value) + } +} + +impl From for ModuleType { + fn from(value: IsmType) -> Self { + match value.0 { + hpl_interface::ism::IsmType::Unused => ModuleType::Unused, + hpl_interface::ism::IsmType::Routing => ModuleType::Routing, + hpl_interface::ism::IsmType::Aggregation => ModuleType::Aggregation, + hpl_interface::ism::IsmType::LegacyMultisig => ModuleType::MessageIdMultisig, + hpl_interface::ism::IsmType::MerkleRootMultisig => ModuleType::MerkleRootMultisig, + hpl_interface::ism::IsmType::MessageIdMultisig => ModuleType::MessageIdMultisig, + hpl_interface::ism::IsmType::Null => ModuleType::Null, + hpl_interface::ism::IsmType::CcipRead => ModuleType::CcipRead, + } + } +} + +pub fn tx_response_to_outcome(response: TxResponse) -> ChainResult { + Ok(TxOutcome { + transaction_id: H256::from_slice(hex::decode(response.txhash)?.as_slice()).into(), + executed: response.code == 0, + gas_used: U256::from(response.gas_used), + gas_price: U256::one(), + }) +} diff --git a/rust/chains/hyperlane-cosmos/src/utils.rs b/rust/chains/hyperlane-cosmos/src/utils.rs new file mode 100644 index 0000000000..898298f438 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/utils.rs @@ -0,0 +1,46 @@ +use std::num::NonZeroU64; + +use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; +use hyperlane_core::ChainResult; +use once_cell::sync::Lazy; + +use crate::grpc::{WasmGrpcProvider, WasmProvider}; + +/// The event attribute key for the contract address. +pub(crate) const CONTRACT_ADDRESS_ATTRIBUTE_KEY: &str = "_contract_address"; +/// Base64 encoded version of the contract address attribute key, i.e. +pub(crate) static CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64: Lazy = + Lazy::new(|| BASE64.encode(CONTRACT_ADDRESS_ATTRIBUTE_KEY)); + +/// Given a lag, returns the block height at the moment. +/// If the lag is None, a block height of None is given, indicating that the +/// tip directly can be used. +pub(crate) async fn get_block_height_for_lag( + provider: &WasmGrpcProvider, + lag: Option, +) -> ChainResult> { + let block_height = match lag { + Some(lag) => { + let tip = provider.latest_block_height().await?; + let block_height = tip - lag.get(); + Some(block_height) + } + None => None, + }; + + Ok(block_height) +} + +#[cfg(test)] +/// Helper function to create a Vec from a JSON string - +/// crate::payloads::general::EventAttribute has a Deserialize impl while +/// cosmrs::tendermint::abci::EventAttribute does not. +pub(crate) fn event_attributes_from_str( + attrs_str: &str, +) -> Vec { + serde_json::from_str::>(attrs_str) + .unwrap() + .into_iter() + .map(|attr| attr.into()) + .collect() +} diff --git a/rust/chains/hyperlane-cosmos/src/validator_announce.rs b/rust/chains/hyperlane-cosmos/src/validator_announce.rs new file mode 100644 index 0000000000..69f7121b88 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/validator_announce.rs @@ -0,0 +1,116 @@ +use async_trait::async_trait; + +use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; +use hyperlane_core::{ + Announcement, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + HyperlaneProvider, SignedType, TxOutcome, ValidatorAnnounce, H160, H256, U256, +}; + +use crate::{ + grpc::{WasmGrpcProvider, WasmProvider}, + payloads::validator_announce::{ + self, AnnouncementRequest, AnnouncementRequestInner, GetAnnounceStorageLocationsRequest, + GetAnnounceStorageLocationsRequestInner, + }, + signers::Signer, + types::tx_response_to_outcome, + ConnectionConf, CosmosProvider, +}; + +/// A reference to a ValidatorAnnounce contract on some Cosmos chain +#[derive(Debug)] +pub struct CosmosValidatorAnnounce { + domain: HyperlaneDomain, + address: H256, + provider: Box, +} + +impl CosmosValidatorAnnounce { + /// create a new instance of CosmosValidatorAnnounce + pub fn new( + conf: ConnectionConf, + locator: ContractLocator, + signer: Option, + ) -> ChainResult { + let provider = WasmGrpcProvider::new(conf.clone(), locator.clone(), signer)?; + + Ok(Self { + domain: locator.domain.clone(), + address: locator.address, + provider: Box::new(provider), + }) + } +} + +impl HyperlaneContract for CosmosValidatorAnnounce { + fn address(&self) -> H256 { + self.address + } +} + +impl HyperlaneChain for CosmosValidatorAnnounce { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(CosmosProvider::new(self.domain.clone())) + } +} + +#[async_trait] +impl ValidatorAnnounce for CosmosValidatorAnnounce { + async fn get_announced_storage_locations( + &self, + validators: &[H256], + ) -> ChainResult>> { + let vss = validators + .iter() + .map(|v| H160::from(*v)) + .map(|v| hex::encode(v.as_bytes())) + .collect::>(); + + let payload = GetAnnounceStorageLocationsRequest { + get_announce_storage_locations: GetAnnounceStorageLocationsRequestInner { + validators: vss, + }, + }; + + let data: Vec = self.provider.wasm_query(payload, None).await?; + let response: validator_announce::GetAnnounceStorageLocationsResponse = + serde_json::from_slice(&data)?; + + Ok(response + .storage_locations + .into_iter() + .map(|v| v.1) + .collect()) + } + + async fn announce( + &self, + announcement: SignedType, + tx_gas_limit: Option, + ) -> ChainResult { + let announce_request = AnnouncementRequest { + announce: AnnouncementRequestInner { + validator: hex::encode(announcement.value.validator), + storage_location: announcement.value.storage_location, + signature: hex::encode(announcement.signature.to_vec()), + }, + }; + + let response: TxResponse = self + .provider + .wasm_send(announce_request, tx_gas_limit) + .await?; + + Ok(tx_response_to_outcome(response)?) + } + + async fn announce_tokens_needed(&self, announcement: SignedType) -> Option { + // TODO: check user balance. For now, just try announcing and + // allow the announce attempt to fail if there are not enough tokens. + Some(0u64.into()) + } +} diff --git a/rust/chains/hyperlane-ethereum/Cargo.toml b/rust/chains/hyperlane-ethereum/Cargo.toml index d13a54e84d..8d6db17f45 100644 --- a/rust/chains/hyperlane-ethereum/Cargo.toml +++ b/rust/chains/hyperlane-ethereum/Cargo.toml @@ -20,6 +20,7 @@ ethers.workspace = true futures-util.workspace = true hex.workspace = true num.workspace = true +num-traits.workspace = true reqwest.workspace = true serde.workspace = true serde_json.workspace = true @@ -31,7 +32,6 @@ url.workspace = true hyperlane-core = { path = "../../hyperlane-core" } ethers-prometheus = { path = "../../ethers-prometheus", features = ["serde"] } -num-traits.workspace = true [build-dependencies] abigen = { path = "../../utils/abigen", features = ["ethers"] } diff --git a/rust/chains/hyperlane-ethereum/src/merkle_tree_hook.rs b/rust/chains/hyperlane-ethereum/src/merkle_tree_hook.rs index 3881b03250..1beffed5bc 100644 --- a/rust/chains/hyperlane-ethereum/src/merkle_tree_hook.rs +++ b/rust/chains/hyperlane-ethereum/src/merkle_tree_hook.rs @@ -130,7 +130,7 @@ where Ok(logs) } - #[instrument(level = "debug", err, ret, skip(self))] + #[instrument(level = "debug", err, skip(self))] async fn get_finalized_block_number(&self) -> ChainResult { Ok(self .provider @@ -147,15 +147,13 @@ impl SequenceIndexer for EthereumMerkleTreeHookIndexer ChainResult<(Option, u32)> { - // The InterchainGasPaymasterIndexerBuilder must return a `SequenceIndexer` type. - // It's fine if only a blanket implementation is provided for EVM chains, since their - // indexing only uses the `Index` trait, which is a supertrait of `SequenceIndexer`. - // TODO: if `SequenceIndexer` turns out to not depend on `Indexer` at all, then the supertrait - // dependency could be removed, even if the builder would still need to return a type that is both - // ``SequenceIndexer` and `Indexer`. let tip = self.get_finalized_block_number().await?; - Ok((None, tip)) + let sequence = self.contract.count().block(u64::from(tip)).call().await?; + Ok((Some(sequence), tip)) } } diff --git a/rust/chains/hyperlane-ethereum/tests/signer_output.rs b/rust/chains/hyperlane-ethereum/tests/signer_output.rs index 1f5ebb42e2..a69629114b 100644 --- a/rust/chains/hyperlane-ethereum/tests/signer_output.rs +++ b/rust/chains/hyperlane-ethereum/tests/signer_output.rs @@ -14,7 +14,7 @@ use hyperlane_core::{ }, test_utils, utils::domain_hash, - Checkpoint, HyperlaneMessage, HyperlaneSignerExt, H160, H256, + HyperlaneMessage, H160, H256, }; /// Output proof to /vector/message.json diff --git a/rust/chains/hyperlane-fuel/src/lib.rs b/rust/chains/hyperlane-fuel/src/lib.rs index 5ce9f0782a..949387870a 100644 --- a/rust/chains/hyperlane-fuel/src/lib.rs +++ b/rust/chains/hyperlane-fuel/src/lib.rs @@ -5,12 +5,10 @@ // TODO: Remove once we start filling things in #![allow(unused_variables)] -pub use interchain_gas::*; -pub use mailbox::*; -pub use multisig_ism::*; -pub use provider::*; -pub use routing_ism::*; -pub use trait_builder::*; +pub use self::{ + interchain_gas::*, mailbox::*, multisig_ism::*, provider::*, routing_ism::*, trait_builder::*, + validator_announce::*, +}; mod contracts; mod conversions; @@ -20,6 +18,7 @@ mod multisig_ism; mod provider; mod routing_ism; mod trait_builder; +mod validator_announce; /// Safe default imports of commonly used traits/types. pub mod prelude { diff --git a/rust/chains/hyperlane-fuel/src/validator_announce.rs b/rust/chains/hyperlane-fuel/src/validator_announce.rs index ea31a09e94..66c39b1c64 100644 --- a/rust/chains/hyperlane-fuel/src/validator_announce.rs +++ b/rust/chains/hyperlane-fuel/src/validator_announce.rs @@ -1,7 +1,8 @@ use async_trait::async_trait; use hyperlane_core::{ - ChainResult, HyperlaneChain, HyperlaneContract, HyperlaneDomain, ValidatorAnnounce, H256, + Announcement, ChainResult, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + HyperlaneProvider, SignedType, TxOutcome, ValidatorAnnounce, H256, U256, }; /// A reference to a ValidatorAnnounce contract on some Fuel chain @@ -18,6 +19,10 @@ impl HyperlaneChain for FuelValidatorAnnounce { fn domain(&self) -> &HyperlaneDomain { todo!() } + + fn provider(&self) -> Box { + todo!() + } } #[async_trait] @@ -28,4 +33,16 @@ impl ValidatorAnnounce for FuelValidatorAnnounce { ) -> ChainResult>> { todo!() } + + async fn announce( + &self, + announcement: SignedType, + tx_gas_limit: Option, + ) -> ChainResult { + todo!() + } + + async fn announce_tokens_needed(&self, announcement: SignedType) -> Option { + todo!() + } } diff --git a/rust/config/mainnet3_config.json b/rust/config/mainnet3_config.json index 6b36f35fbe..70f804cc91 100644 --- a/rust/config/mainnet3_config.json +++ b/rust/config/mainnet3_config.json @@ -45,7 +45,7 @@ "mailbox": "0x979Ca5202784112f4738403dBec5D0F3B9daabB9", "validatorAnnounce": "0x1df063280C4166AF9a725e3828b4dAC6c7113B08", "index": { - "from": 145551152 + "from": 143699718 } }, "avalanche": { @@ -97,7 +97,7 @@ "mailbox": "0xFf06aFcaABaDDd1fb08371f9ccA15D73D51FeBD6", "validatorAnnounce": "0x9Cad0eC82328CEE2386Ec14a12E81d070a27712f", "index": { - "from": 37133307 + "from": 36881761 } }, "base": { @@ -150,7 +150,7 @@ "protocolFee": "0x99ca8c74cE7Cfa9d72A51fbb05F9821f5f826b3a", "validatorAnnounce": "0x182E8d7c5F1B06201b102123FC7dF0EaeB445a7B", "index": { - "from": 5959667 + "from": 5702757 } }, "bsc": { @@ -205,7 +205,7 @@ "protocolFee": "0xA8Aa5f14a5463a78E45CC068F11c867949F3E367", "validatorAnnounce": "0x7024078130D9c2100fEA474DAD009C2d1703aCcd", "index": { - "from": 33068482 + "from": 32897848 } }, "celo": { @@ -258,7 +258,7 @@ "mailbox": "0x50da3B3907A08a24fe4999F4Dcf337E8dC7954bb", "validatorAnnounce": "0xCeF677b65FDaA6804d4403083bb12B8dB3991FE1", "index": { - "from": 22208016 + "from": 22105253 } }, "ethereum": { @@ -318,7 +318,7 @@ "mailbox": "0xc005dc82818d67AF737725bD4bf75435d065D239", "validatorAnnounce": "0xCe74905e51497b4adD3639366708b821dcBcff96", "index": { - "from": 18466263 + "from": 18423787 } }, "gnosis": { @@ -370,7 +370,79 @@ "protocolFee": "0x9c2214467Daf9e2e1F45b36d08ce0b9C65BFeA88", "validatorAnnounce": "0x87ED6926abc9E38b9C7C19f835B41943b622663c", "index": { - "from": 30715963 + "from": 30623434 + } + }, + "mantapacific": { + "protocol": "ethereum", + "domainId": 169, + "chainId": 169, + "name": "mantapacific", + "displayName": "Manta Pacific", + "displayNameShort": "Manta", + "nativeToken": { + "name": "Ether", + "symbol": "ETH", + "decimals": 18 + }, + "blocks": { + "confirmations": 1, + "reorgPeriod": 0, + "estimateBlockTime": 3 + }, + "rpcUrls": [ + { + "http": "https://pacific-rpc.manta.network/http" + } + ], + "isTestnet": false, + "merkleRootMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "messageIdMultisigIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "aggregationIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "aggregationHookFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "routingIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "domainRoutingIsm": "0xDEed16fe4b1c9b2a93483EDFf34C77A9b57D31Ff", + "storageGasOracle": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "aggregationHook": "0x8464aF853363B8d6844070F68b0AB34Cb6523d0F", + "protocolFee": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", + "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "index": { + "from": 437384 + } + }, + "neutron": { + "name": "neutron", + "domainId": "1853125230", + "chainId": "neutron-1", + "mailbox": "0x848426d50eb2104d5c6381ec63757930b1c14659c40db8b8081e516e7c5238fc", + "interchainGasPaymaster": "0x504ee9ac43ec5814e00c7d21869a90ec52becb489636bdf893b7df9d606b5d67", + "validatorAnnounce": "0xf3aa0d652226e21ae35cd9035c492ae41725edc9036edf0d6a48701b153b90a0", + "merkleTreeHook": "0xcd30a0001cc1f436c41ef764a712ebabc5a144140e3fd03eafe64a9a24e4e27c", + "protocol": "cosmos", + "finalityBlocks": 1, + "rpcUrls": [ + { + "http": "https://rpc-kralum.neutron-1.neutron.org" + } + ], + "grpcUrl": "https://grpc-kralum.neutron-1.neutron.org:80", + "canonicalAsset": "untrn", + "prefix": "neutron", + "index": { + "from": 4000000, + "chunk": 100000 + }, + "blocks": { + "reorgPeriod": 1 + }, + "signer": { + "type": "cosmosKey", + "key": "0x5486418967eabc770b0fcb995f7ef6d9a72f7fc195531ef76c5109f44f51af26", + "prefix": "neutron" } }, "moonbeam": { @@ -417,7 +489,7 @@ "protocolFee": "0xCd3e29A9D293DcC7341295996a118913F7c582c0", "validatorAnnounce": "0x8c1001eBee6F25b31863A55EadfF149aF88B356F", "index": { - "from": 4763137 + "from": 4720894 } }, "optimism": { @@ -465,7 +537,7 @@ "mailbox": "0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D", "validatorAnnounce": "0x30f5b08e01808643221528BB2f7953bf2830Ef38", "index": { - "from": 111554952 + "from": 111298042 } }, "polygon": { @@ -524,7 +596,7 @@ "protocolFee": "0xF8F3629e308b4758F8396606405989F8D8C9c578", "validatorAnnounce": "0x454E1a1E1CA8B51506090f1b5399083658eA4Fc5", "index": { - "from": 49352047 + "from": 49114872 } }, "polygonzkevm": { @@ -576,7 +648,7 @@ "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", "index": { - "from": 6789061 + "from": 6581140 } }, "scroll": { diff --git a/rust/config/test-sealevel-keys/test_deployer-account.json b/rust/config/test-sealevel-keys/test_deployer-account.json deleted file mode 100644 index 1541f52385..0000000000 --- a/rust/config/test-sealevel-keys/test_deployer-account.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "pubkey": "E9VrvAdGRvCguN2XgXsgu9PNmMM3vZsU8LSUrM68j8ty", - "account": { - "lamports": 500000000000000000, - "data": [ - "", - "base64" - ], - "owner": "11111111111111111111111111111111", - "executable": false, - "rentEpoch": 0 - } -} \ No newline at end of file diff --git a/rust/config/test-sealevel-keys/test_deployer-keypair.json b/rust/config/test-sealevel-keys/test_deployer-keypair.json deleted file mode 100644 index 36e1ec6786..0000000000 --- a/rust/config/test-sealevel-keys/test_deployer-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[137,43,246,148,154,244,35,62,98,248,84,203,54,24,188,26,62,227,52,29,199,26,218,8,196,213,222,202,35,154,207,79,195,85,53,151,7,182,83,94,59,5,131,252,40,75,87,11,243,118,71,59,195,222,212,148,179,233,253,121,97,210,114,98] \ No newline at end of file diff --git a/rust/config/test_sealevel_config.json b/rust/config/test_sealevel_config.json deleted file mode 100644 index 5c127dcc3d..0000000000 --- a/rust/config/test_sealevel_config.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "chains": { - "sealeveltest1": { - "name": "sealeveltest1", - "chainId": 13375, - "domainId": 13375, - "mailbox": "692KZJaoe2KRcD6uhCQDLLXnLNA5ZLnfvdqjE4aX9iu1", - "interchainGasPaymaster": "DrFtxirPPsfdY4HQiNZj2A9o4Ux7JaL3gELANgAoihhp", - "validatorAnnounce": "DH43ae1LwemXAboWwSh8zc9pG8j72gKUEXNi57w8fEnn", - "protocol": "sealevel", - "blocks": { - "reorgPeriod": 0, - "confirmations": 0 - }, - "rpcUrls": [ - { - "http": "http://localhost:8899" - } - ], - "index": { - "from": 1, - "mode": "sequence" - } - }, - "sealeveltest2": { - "name": "sealeveltest2", - "chainId": 13376, - "domainId": 13376, - "mailbox": "9tCUWNjpqcf3NUSrtp7vquYVCwbEByvLjZUrhG5dgvhj", - "interchainGasPaymaster": "G5rGigZBL8NmxCaukK2CAKr9Jq4SUfAhsjzeri7GUraK", - "validatorAnnounce": "3Uo5j2Bti9aZtrDqJmAyuwiFaJFPFoNL5yxTpVCNcUhb", - "protocol": "sealevel", - "blocks": { - "reorgPeriod": 0, - "confirmations": 0 - }, - "rpcUrls": [ - { - "http": "http://localhost:8899" - } - ], - "index": { - "from": 1, - "mode": "sequence" - } - } - } -} diff --git a/rust/config/testnet4_config.json b/rust/config/testnet4_config.json index d0fbd12598..12621e6035 100644 --- a/rust/config/testnet4_config.json +++ b/rust/config/testnet4_config.json @@ -1007,6 +1007,32 @@ "index": { "from": 4558491 } + }, + "neutrontestnet": { + "name": "neutrontestnet", + "domainId": "33333", + "chainId": "duality-devnet", + "mailbox": "0xdb33c78ca39541dd740659fbfd86fdd601fe7225f10f26e87595c5b8df6bdcda", + "interchainGasPaymaster": "0xae38a168ced2b1cdafd7da5de2fbd22749b1ab52e88cd0b121f750a6c20a2814", + "validatorAnnounce": "0x29d5f702a35d4135d98abf52c62ddc0cfd74001663ec14d284edff7d0419fb58", + "merkleTreeHook": "0xb6aac0c4650129ded8e645e6ef8dcbba710623b826e5a1dae34158a27247c668", + "protocol": "cosmos", + "finalityBlocks": 1, + "rpcUrls": [ + { + "http": "http://54.149.31.83:26657" + } + ], + "grpcUrl": "http://52.43.22.152:9090", + "canonicalAsset": "token", + "prefix": "dual", + "index": { + "from": 1, + "chunk": 100000 + }, + "blocks": { + "reorgPeriod": 1 + } } }, "defaultRpcConsensusType": "fallback" diff --git a/rust/hyperlane-base/Cargo.toml b/rust/hyperlane-base/Cargo.toml index 3d28ab0e05..02d870e644 100644 --- a/rust/hyperlane-base/Cargo.toml +++ b/rust/hyperlane-base/Cargo.toml @@ -27,6 +27,7 @@ prometheus.workspace = true rocksdb.workspace = true serde.workspace = true serde_json.workspace = true +solana-sdk.worksapce = true static_assertions.workspace = true tempfile = { workspace = true, optional = true } thiserror.workspace = true @@ -35,6 +36,7 @@ tracing-error.workspace = true tracing-futures.workspace = true tracing-subscriber = { workspace = true, features = ["json", "ansi"] } tracing.workspace = true +url.workspace = true warp.workspace = true backtrace = { workspace = true, optional = true } @@ -45,6 +47,7 @@ hyperlane-core = { path = "../hyperlane-core", features = ["agent", "float"] } hyperlane-ethereum = { path = "../chains/hyperlane-ethereum" } hyperlane-fuel = { path = "../chains/hyperlane-fuel" } hyperlane-sealevel = { path = "../chains/hyperlane-sealevel" } +hyperlane-cosmos = { path = "../chains/hyperlane-cosmos"} hyperlane-test = { path = "../hyperlane-test" } # dependency version is determined by etheres diff --git a/rust/hyperlane-base/src/contract_sync/cursor.rs b/rust/hyperlane-base/src/contract_sync/cursor.rs index 14cff9b02a..cbbd393dbd 100644 --- a/rust/hyperlane-base/src/contract_sync/cursor.rs +++ b/rust/hyperlane-base/src/contract_sync/cursor.rs @@ -10,9 +10,9 @@ use async_trait::async_trait; use derive_new::new; use eyre::Result; use hyperlane_core::{ - ChainCommunicationError, ChainResult, ContractSyncCursor, CursorAction, HyperlaneMessage, - HyperlaneMessageStore, HyperlaneWatermarkedLogStore, IndexMode, Indexer, LogMeta, - SequenceIndexer, + ChainCommunicationError, ChainResult, ContractSyncCursor, CursorAction, + HyperlaneSequenceIndexerStore, HyperlaneWatermarkedLogStore, IndexMode, Indexer, LogMeta, + SequenceIndexer, Sequenced, }; use tokio::time::sleep; use tracing::{debug, warn}; @@ -25,11 +25,11 @@ const ETA_TIME_WINDOW: f64 = 2. * 60.; const MAX_SEQUENCE_RANGE: u32 = 100; /// A struct that holds the data needed for forwards and backwards -/// message sync cursors. +/// sequence sync cursors. #[derive(Debug, new)] -pub(crate) struct MessageSyncCursor { - indexer: Arc>, - db: Arc, +pub(crate) struct SequenceSyncCursor { + indexer: Arc>, + db: Arc>, sync_state: SyncState, } @@ -129,41 +129,34 @@ impl SyncState { } } -impl MessageSyncCursor { - async fn retrieve_message_by_nonce(&self, nonce: u32) -> Option { - if let Ok(Some(message)) = self.db.retrieve_message_by_nonce(nonce).await { - Some(message) - } else { - None - } +impl SequenceSyncCursor { + async fn retrieve_by_sequence(&self, sequence: u32) -> Option { + self.db.retrieve_by_sequence(sequence).await.ok().flatten() } - async fn retrieve_dispatched_block_number(&self, nonce: u32) -> Option { - if let Ok(Some(block_number)) = self.db.retrieve_dispatched_block_number(nonce).await { - Some(u32::try_from(block_number).unwrap()) - } else { - None - } + async fn retrieve_log_block_number(&self, sequence: u32) -> Option { + self.db + .retrieve_log_block_number(sequence) + .await + .ok() + .flatten() + .map(|num| u32::try_from(num).unwrap()) } - async fn update( - &mut self, - logs: Vec<(HyperlaneMessage, LogMeta)>, - prev_sequence: u32, - ) -> Result<()> { - // If we found messages, but did *not* find the message we were looking for, - // we need to rewind to the block at which we found the last message. + async fn update(&mut self, logs: Vec<(T, LogMeta)>, prev_sequence: u32) -> Result<()> { + // If we found logs, but did *not* find the log we were looking for, + // we need to rewind to the block at which we found the last log. if !logs.is_empty() && !logs .iter() - .any(|m| m.0.nonce == self.sync_state.next_sequence) + .any(|m| m.0.sequence() == self.sync_state.next_sequence) { - warn!(next_nonce=?self.sync_state.next_sequence, "Target nonce not found, rewinding"); - // If the previous nonce has been synced, rewind to the block number + warn!(next_sequence=?self.sync_state.next_sequence, "Target sequence not found, rewinding"); + // If the previous sequence has been synced, rewind to the block number // at which it was dispatched. Otherwise, rewind all the way back to the start block. - if let Some(block_number) = self.retrieve_dispatched_block_number(prev_sequence).await { + if let Some(block_number) = self.retrieve_log_block_number(prev_sequence).await { self.sync_state.next_block = block_number; - warn!(block_number, "Rewound to previous known message"); + warn!(block_number, "Rewound to previous known sequenced log"); } else { self.sync_state.next_block = self.sync_state.start_block; } @@ -174,15 +167,15 @@ impl MessageSyncCursor { } } -/// A MessageSyncCursor that syncs forwards in perpetuity. -pub(crate) struct ForwardMessageSyncCursor { - cursor: MessageSyncCursor, +/// A SequenceSyncCursor that syncs forwards in perpetuity. +pub(crate) struct ForwardSequenceSyncCursor { + cursor: SequenceSyncCursor, } -impl ForwardMessageSyncCursor { +impl ForwardSequenceSyncCursor { pub fn new( - indexer: Arc>, - db: Arc, + indexer: Arc>, + db: Arc>, chunk_size: u32, start_block: u32, next_block: u32, @@ -190,7 +183,7 @@ impl ForwardMessageSyncCursor { next_sequence: u32, ) -> Self { Self { - cursor: MessageSyncCursor::new( + cursor: SequenceSyncCursor::new( indexer, db, SyncState::new( @@ -206,17 +199,17 @@ impl ForwardMessageSyncCursor { } async fn get_next_range(&mut self) -> ChainResult>> { - // Check if any new messages have been inserted into the DB, + // Check if any new logs have been inserted into the DB, // and update the cursor accordingly. while self .cursor - .retrieve_message_by_nonce(self.cursor.sync_state.next_sequence) + .retrieve_by_sequence(self.cursor.sync_state.next_sequence) .await .is_some() { if let Some(block_number) = self .cursor - .retrieve_dispatched_block_number(self.cursor.sync_state.next_sequence) + .retrieve_log_block_number(self.cursor.sync_state.next_sequence) .await { debug!(next_block = block_number, "Fast forwarding next block"); @@ -224,8 +217,8 @@ impl ForwardMessageSyncCursor { self.cursor.sync_state.next_block = block_number; } debug!( - next_nonce = self.cursor.sync_state.next_sequence + 1, - "Fast forwarding next nonce" + next_sequence = self.cursor.sync_state.next_sequence + 1, + "Fast forwarding next sequence" ); self.cursor.sync_state.next_sequence += 1; } @@ -236,7 +229,7 @@ impl ForwardMessageSyncCursor { let cursor_count = self.cursor.sync_state.next_sequence; Ok(match cursor_count.cmp(&mailbox_count) { Ordering::Equal => { - // We are synced up to the latest nonce so we don't need to index anything. + // We are synced up to the latest sequence so we don't need to index anything. // We update our next block number accordingly. self.cursor.sync_state.next_block = tip; None @@ -259,7 +252,7 @@ impl ForwardMessageSyncCursor { } #[async_trait] -impl ContractSyncCursor for ForwardMessageSyncCursor { +impl ContractSyncCursor for ForwardSequenceSyncCursor { async fn next_action(&mut self) -> ChainResult<(CursorAction, Duration)> { // TODO: Fix ETA calculation let eta = Duration::from_secs(0); @@ -278,29 +271,29 @@ impl ContractSyncCursor for ForwardMessageSyncCursor { /// If the previous block has been synced, rewind to the block number /// at which it was dispatched. /// Otherwise, rewind all the way back to the start block. - async fn update(&mut self, logs: Vec<(HyperlaneMessage, LogMeta)>) -> Result<()> { - let prev_nonce = self.cursor.sync_state.next_sequence.saturating_sub(1); - // We may wind up having re-indexed messages that are previous to the nonce that we are looking for. - // We should not consider these messages when checking for continuity errors. + async fn update(&mut self, logs: Vec<(T, LogMeta)>) -> Result<()> { + let prev_sequence = self.cursor.sync_state.next_sequence.saturating_sub(1); + // We may wind up having re-indexed logs that are previous to the sequence that we are looking for. + // We should not consider these logs when checking for continuity errors. let filtered_logs = logs .into_iter() - .filter(|m| m.0.nonce >= self.cursor.sync_state.next_sequence) + .filter(|m| m.0.sequence() >= self.cursor.sync_state.next_sequence) .collect(); - self.cursor.update(filtered_logs, prev_nonce).await + self.cursor.update(filtered_logs, prev_sequence).await } } -/// A MessageSyncCursor that syncs backwards to sequence (nonce) zero. -pub(crate) struct BackwardMessageSyncCursor { - cursor: MessageSyncCursor, +/// A SequenceSyncCursor that syncs backwards to sequence zero. +pub(crate) struct BackwardSequenceSyncCursor { + cursor: SequenceSyncCursor, synced: bool, } -impl BackwardMessageSyncCursor { +impl BackwardSequenceSyncCursor { #[allow(clippy::too_many_arguments)] pub fn new( - indexer: Arc>, - db: Arc, + indexer: Arc>, + db: Arc>, chunk_size: u32, start_block: u32, next_block: u32, @@ -309,7 +302,7 @@ impl BackwardMessageSyncCursor { synced: bool, ) -> Self { Self { - cursor: MessageSyncCursor::new( + cursor: SequenceSyncCursor::new( indexer, db, SyncState::new( @@ -326,12 +319,12 @@ impl BackwardMessageSyncCursor { } async fn get_next_range(&mut self) -> ChainResult>> { - // Check if any new messages have been inserted into the DB, + // Check if any new logs have been inserted into the DB, // and update the cursor accordingly. while !self.synced { if self .cursor - .retrieve_message_by_nonce(self.cursor.sync_state.next_sequence) + .retrieve_by_sequence(self.cursor.sync_state.next_sequence) .await .is_none() { @@ -345,7 +338,7 @@ impl BackwardMessageSyncCursor { if let Some(block_number) = self .cursor - .retrieve_dispatched_block_number(self.cursor.sync_state.next_sequence) + .retrieve_log_block_number(self.cursor.sync_state.next_sequence) .await { // It's possible that eth_getLogs dropped logs from this block, therefore we cannot do block_number - 1. @@ -367,13 +360,13 @@ impl BackwardMessageSyncCursor { /// If the previous block has been synced, rewind to the block number /// at which it was dispatched. /// Otherwise, rewind all the way back to the start block. - async fn update(&mut self, logs: Vec<(HyperlaneMessage, LogMeta)>) -> Result<()> { + async fn update(&mut self, logs: Vec<(T, LogMeta)>) -> Result<()> { let prev_sequence = self.cursor.sync_state.next_sequence.saturating_add(1); - // We may wind up having re-indexed messages that are previous to the sequence (nonce) that we are looking for. - // We should not consider these messages when checking for continuity errors. + // We may wind up having re-indexed logs that are previous to the sequence that we are looking for. + // We should not consider these logs when checking for continuity errors. let filtered_logs = logs .into_iter() - .filter(|m| m.0.nonce <= self.cursor.sync_state.next_sequence) + .filter(|m| m.0.sequence() <= self.cursor.sync_state.next_sequence) .collect(); self.cursor.update(filtered_logs, prev_sequence).await } @@ -385,43 +378,43 @@ pub enum SyncDirection { Backward, } -/// A MessageSyncCursor that syncs forwards in perpetuity. -pub(crate) struct ForwardBackwardMessageSyncCursor { - forward: ForwardMessageSyncCursor, - backward: BackwardMessageSyncCursor, +/// A SequenceSyncCursor that syncs forwards in perpetuity. +pub(crate) struct ForwardBackwardSequenceSyncCursor { + forward: ForwardSequenceSyncCursor, + backward: BackwardSequenceSyncCursor, direction: SyncDirection, } -impl ForwardBackwardMessageSyncCursor { +impl ForwardBackwardSequenceSyncCursor { /// Construct a new contract sync helper. pub async fn new( - indexer: Arc>, - db: Arc, + indexer: Arc>, + db: Arc>, chunk_size: u32, mode: IndexMode, ) -> Result { - let (count, tip) = indexer.sequence_and_tip().await?; - let count = count.ok_or(ChainCommunicationError::from_other_str( - "Failed to query message count", + let (sequence, tip) = indexer.sequence_and_tip().await?; + let sequence = sequence.ok_or(ChainCommunicationError::from_other_str( + "Failed to query sequence", ))?; - let forward_cursor = ForwardMessageSyncCursor::new( + let forward_cursor = ForwardSequenceSyncCursor::new( indexer.clone(), db.clone(), chunk_size, tip, tip, mode, - count, + sequence, ); - let backward_cursor = BackwardMessageSyncCursor::new( + let backward_cursor = BackwardSequenceSyncCursor::new( indexer.clone(), db.clone(), chunk_size, tip, tip, mode, - count.saturating_sub(1), - count == 0, + sequence.saturating_sub(1), + sequence == 0, ); Ok(Self { forward: forward_cursor, @@ -432,7 +425,7 @@ impl ForwardBackwardMessageSyncCursor { } #[async_trait] -impl ContractSyncCursor for ForwardBackwardMessageSyncCursor { +impl ContractSyncCursor for ForwardBackwardSequenceSyncCursor { async fn next_action(&mut self) -> ChainResult<(CursorAction, Duration)> { // TODO: Proper ETA for backwards sync let eta = Duration::from_secs(0); @@ -454,7 +447,7 @@ impl ContractSyncCursor for ForwardBackwardMessageSyncCursor { self.forward.cursor.sync_state.next_block.saturating_sub(1) } - async fn update(&mut self, logs: Vec<(HyperlaneMessage, LogMeta)>) -> Result<()> { + async fn update(&mut self, logs: Vec<(T, LogMeta)>) -> Result<()> { match self.direction { SyncDirection::Forward => self.forward.update(logs).await, SyncDirection::Backward => self.backward.update(logs).await, diff --git a/rust/hyperlane-base/src/contract_sync/mod.rs b/rust/hyperlane-base/src/contract_sync/mod.rs index d569d2c14f..3968ad9f57 100644 --- a/rust/hyperlane-base/src/contract_sync/mod.rs +++ b/rust/hyperlane-base/src/contract_sync/mod.rs @@ -4,8 +4,8 @@ use cursor::*; use derive_new::new; use hyperlane_core::{ utils::fmt_sync_time, ContractSyncCursor, CursorAction, HyperlaneDomain, HyperlaneLogStore, - HyperlaneMessage, HyperlaneMessageStore, HyperlaneWatermarkedLogStore, Indexer, - SequenceIndexer, + HyperlaneSequenceIndexerStore, HyperlaneWatermarkedLogStore, Indexer, SequenceIndexer, + Sequenced, }; pub use metrics::ContractSyncMetrics; use tokio::time::sleep; @@ -121,20 +121,17 @@ where } } -/// A ContractSync for syncing messages using a MessageSyncCursor -pub type MessageContractSync = ContractSync< - HyperlaneMessage, - Arc, - Arc>, ->; -impl MessageContractSync { +/// A ContractSync for syncing messages using a SequenceSyncCursor +pub type SequencedDataContractSync = + ContractSync>, Arc>>; +impl SequencedDataContractSync { /// Returns a new cursor to be used for syncing dispatched messages from the indexer pub async fn forward_message_sync_cursor( &self, index_settings: IndexSettings, next_nonce: u32, - ) -> Box> { - Box::new(ForwardMessageSyncCursor::new( + ) -> Box> { + Box::new(ForwardSequenceSyncCursor::new( self.indexer.clone(), self.db.clone(), index_settings.chunk_size, @@ -149,9 +146,9 @@ impl MessageContractSync { pub async fn forward_backward_message_sync_cursor( &self, index_settings: IndexSettings, - ) -> Box> { + ) -> Box> { Box::new( - ForwardBackwardMessageSyncCursor::new( + ForwardBackwardSequenceSyncCursor::new( self.indexer.clone(), self.db.clone(), index_settings.chunk_size, diff --git a/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs b/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs index 4c496d741d..807645beb0 100644 --- a/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs +++ b/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs @@ -4,9 +4,9 @@ use paste::paste; use tracing::{debug, instrument, trace}; use hyperlane_core::{ - GasPaymentKey, HyperlaneDomain, HyperlaneLogStore, HyperlaneMessage, HyperlaneMessageStore, - HyperlaneWatermarkedLogStore, InterchainGasExpenditure, InterchainGasPayment, - InterchainGasPaymentMeta, LogMeta, MerkleTreeInsertion, H256, + GasPaymentKey, HyperlaneDomain, HyperlaneLogStore, HyperlaneMessage, + HyperlaneSequenceIndexerStore, HyperlaneWatermarkedLogStore, InterchainGasExpenditure, + InterchainGasPayment, InterchainGasPaymentMeta, LogMeta, MerkleTreeInsertion, H256, }; use super::{ @@ -28,6 +28,8 @@ const PENDING_MESSAGE_RETRY_COUNT_FOR_MESSAGE_ID: &str = "pending_message_retry_count_for_message_id_"; const MERKLE_TREE_INSERTION: &str = "merkle_tree_insertion_"; const MERKLE_LEAF_INDEX_BY_MESSAGE_ID: &str = "merkle_leaf_index_by_message_id_"; +const MERKLE_TREE_INSERTION_BLOCK_NUMBER_BY_LEAF_INDEX: &str = + "merkle_tree_insertion_block_number_by_leaf_index_"; const LATEST_INDEXED_GAS_PAYMENT_BLOCK: &str = "latest_indexed_gas_payment_block"; type DbResult = std::result::Result; @@ -137,17 +139,27 @@ impl HyperlaneRocksDB { } /// Store the merkle tree insertion event, and also store a mapping from message_id to leaf_index - pub fn process_tree_insertion(&self, insertion: &MerkleTreeInsertion) -> DbResult { + pub fn process_tree_insertion( + &self, + insertion: &MerkleTreeInsertion, + insertion_block_number: u64, + ) -> DbResult { if let Ok(Some(_)) = self.retrieve_merkle_tree_insertion_by_leaf_index(&insertion.index()) { debug!(insertion=?insertion, "Tree insertion already stored in db"); return Ok(false); } + // even if double insertions are ok, store the leaf by `leaf_index` (guaranteed to be unique) // rather than by `message_id` (not guaranteed to be recurring), so that leaves can be retrieved // based on insertion order. self.store_merkle_tree_insertion_by_leaf_index(&insertion.index(), insertion)?; self.store_merkle_leaf_index_by_message_id(&insertion.message_id(), &insertion.index())?; + + self.store_merkle_tree_insertion_block_number_by_leaf_index( + &insertion.index(), + &insertion_block_number, + )?; // Return true to indicate the tree insertion was processed Ok(true) } @@ -259,8 +271,8 @@ impl HyperlaneLogStore for HyperlaneRocksDB { #[instrument(skip_all)] async fn store_logs(&self, leaves: &[(MerkleTreeInsertion, LogMeta)]) -> Result { let mut insertions = 0; - for (insertion, _meta) in leaves { - if self.process_tree_insertion(insertion)? { + for (insertion, meta) in leaves { + if self.process_tree_insertion(insertion, meta.block_number)? { insertions += 1; } } @@ -269,16 +281,31 @@ impl HyperlaneLogStore for HyperlaneRocksDB { } #[async_trait] -impl HyperlaneMessageStore for HyperlaneRocksDB { - /// Gets a message by nonce. - async fn retrieve_message_by_nonce(&self, nonce: u32) -> Result> { - let message = self.retrieve_message_by_nonce(nonce)?; +impl HyperlaneSequenceIndexerStore for HyperlaneRocksDB { + /// Gets data by its sequence. + async fn retrieve_by_sequence(&self, sequence: u32) -> Result> { + let message = self.retrieve_message_by_nonce(sequence)?; Ok(message) } - /// Retrieve dispatched block number by message nonce - async fn retrieve_dispatched_block_number(&self, nonce: u32) -> Result> { - let number = self.retrieve_dispatched_block_number_by_nonce(&nonce)?; + /// Gets the block number at which the log occurred. + async fn retrieve_log_block_number(&self, sequence: u32) -> Result> { + let number = self.retrieve_dispatched_block_number_by_nonce(&sequence)?; + Ok(number) + } +} + +#[async_trait] +impl HyperlaneSequenceIndexerStore for HyperlaneRocksDB { + /// Gets data by its sequence. + async fn retrieve_by_sequence(&self, sequence: u32) -> Result> { + let insertion = self.retrieve_merkle_tree_insertion_by_leaf_index(&sequence)?; + Ok(insertion) + } + + /// Gets the block number at which the log occurred. + async fn retrieve_log_block_number(&self, sequence: u32) -> Result> { + let number = self.retrieve_merkle_tree_insertion_block_number_by_leaf_index(&sequence)?; Ok(number) } } @@ -356,3 +383,10 @@ make_store_and_retrieve!( H256, u32 ); +make_store_and_retrieve!( + pub, + merkle_tree_insertion_block_number_by_leaf_index, + MERKLE_TREE_INSERTION_BLOCK_NUMBER_BY_LEAF_INDEX, + u32, + u64 +); diff --git a/rust/hyperlane-base/src/settings/base.rs b/rust/hyperlane-base/src/settings/base.rs index efdda4e834..135d80ea0b 100644 --- a/rust/hyperlane-base/src/settings/base.rs +++ b/rust/hyperlane-base/src/settings/base.rs @@ -3,14 +3,15 @@ use std::{collections::HashMap, fmt::Debug, sync::Arc}; use eyre::{eyre, Context, Result}; use futures_util::future::try_join_all; use hyperlane_core::{ - Delivery, HyperlaneChain, HyperlaneDomain, HyperlaneMessageStore, HyperlaneProvider, - HyperlaneWatermarkedLogStore, InterchainGasPaymaster, InterchainGasPayment, Mailbox, - MerkleTreeHook, MerkleTreeInsertion, MultisigIsm, ValidatorAnnounce, H256, + Delivery, HyperlaneChain, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, + HyperlaneSequenceIndexerStore, HyperlaneWatermarkedLogStore, InterchainGasPaymaster, + InterchainGasPayment, Mailbox, MerkleTreeHook, MerkleTreeInsertion, MultisigIsm, + ValidatorAnnounce, H256, }; use crate::{ settings::{chains::ChainConf, trace::TracingConfig}, - ContractSync, ContractSyncMetrics, CoreMetrics, HyperlaneAgentCore, MessageContractSync, + ContractSync, ContractSyncMetrics, CoreMetrics, HyperlaneAgentCore, SequencedDataContractSync, WatermarkContractSync, }; @@ -183,7 +184,7 @@ impl Settings { build_contract_fns!(build_validator_announce, build_validator_announces -> dyn ValidatorAnnounce); build_contract_fns!(build_provider, build_providers -> dyn HyperlaneProvider); build_indexer_fns!(build_delivery_indexer, build_delivery_indexers -> dyn HyperlaneWatermarkedLogStore, WatermarkContractSync); - build_indexer_fns!(build_message_indexer, build_message_indexers -> dyn HyperlaneMessageStore, MessageContractSync); + build_indexer_fns!(build_message_indexer, build_message_indexers -> dyn HyperlaneSequenceIndexerStore, SequencedDataContractSync); build_indexer_fns!(build_interchain_gas_payment_indexer, build_interchain_gas_payment_indexers -> dyn HyperlaneWatermarkedLogStore, WatermarkContractSync); - build_indexer_fns!(build_merkle_tree_hook_indexer, build_merkle_tree_hook_indexers -> dyn HyperlaneWatermarkedLogStore, WatermarkContractSync); + build_indexer_fns!(build_merkle_tree_hook_indexer, build_merkle_tree_hook_indexers -> dyn HyperlaneSequenceIndexerStore, SequencedDataContractSync); } diff --git a/rust/hyperlane-base/src/settings/chains.rs b/rust/hyperlane-base/src/settings/chains.rs index 4596d2867c..e1010d5fd8 100644 --- a/rust/hyperlane-base/src/settings/chains.rs +++ b/rust/hyperlane-base/src/settings/chains.rs @@ -1,10 +1,11 @@ +use ethers::prelude::Selector; use std::collections::HashMap; -use ethers::prelude::Selector; +use eyre::{eyre, Context, Result}; + use ethers_prometheus::middleware::{ ChainInfo, ContractInfo, PrometheusMiddlewareConf, WalletInfo, }; -use eyre::{eyre, Context, Result}; use hyperlane_core::{ AggregationIsm, CcipReadIsm, ContractLocator, HyperlaneAbi, HyperlaneDomain, HyperlaneDomainProtocol, HyperlaneMessage, HyperlaneProvider, HyperlaneSigner, IndexMode, @@ -12,6 +13,7 @@ use hyperlane_core::{ MerkleTreeHook, MerkleTreeInsertion, MultisigIsm, RoutingIsm, SequenceIndexer, ValidatorAnnounce, H256, }; +use hyperlane_cosmos as h_cosmos; use hyperlane_ethereum::{ self as h_eth, BuildableWithProvider, EthereumInterchainGasPaymasterAbi, EthereumMailboxAbi, EthereumValidatorAnnounceAbi, @@ -20,7 +22,7 @@ use hyperlane_fuel as h_fuel; use hyperlane_sealevel as h_sealevel; use crate::{ - settings::signers::{BuildableWithSignerConf, SignerConf}, + settings::signers::{BuildableWithSignerConf, ChainSigner, SignerConf}, CoreMetrics, }; @@ -55,6 +57,8 @@ pub enum ChainConnectionConf { Fuel(h_fuel::ConnectionConf), /// Sealevel configuration. Sealevel(h_sealevel::ConnectionConf), + /// Cosmos configuration. + Cosmos(h_cosmos::ConnectionConf), } impl ChainConnectionConf { @@ -64,6 +68,7 @@ impl ChainConnectionConf { Self::Ethereum(_) => HyperlaneDomainProtocol::Ethereum, Self::Fuel(_) => HyperlaneDomainProtocol::Fuel, Self::Sealevel(_) => HyperlaneDomainProtocol::Sealevel, + Self::Cosmos(_) => HyperlaneDomainProtocol::Cosmos, } } } @@ -112,6 +117,7 @@ impl ChainConf { } ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(_) => todo!(), + ChainConnectionConf::Cosmos(_) => todo!(), } .context(ctx) } @@ -138,6 +144,12 @@ impl ChainConf { .map(|m| Box::new(m) as Box) .map_err(Into::into) } + ChainConnectionConf::Cosmos(conf) => { + let signer = self.cosmos_signer().await.context(ctx)?; + h_cosmos::CosmosMailbox::new(conf.clone(), locator.clone(), signer.clone()) + .map(|m| Box::new(m) as Box) + .map_err(Into::into) + } } .context(ctx) } @@ -169,6 +181,13 @@ impl ChainConf { .map(|m| Box::new(m) as Box) .map_err(Into::into) } + ChainConnectionConf::Cosmos(conf) => { + let signer = self.cosmos_signer().await.context(ctx)?; + let hook = + h_cosmos::CosmosMerkleTreeHook::new(conf.clone(), locator.clone(), signer)?; + + Ok(Box::new(hook) as Box) + } } .context(ctx) } @@ -193,12 +212,21 @@ impl ChainConf { ) .await } - ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(conf) => { let indexer = Box::new(h_sealevel::SealevelMailboxIndexer::new(conf, locator)?); Ok(indexer as Box>) } + ChainConnectionConf::Cosmos(conf) => { + let signer = self.cosmos_signer().await.context(ctx)?; + let indexer = Box::new(h_cosmos::CosmosMailboxIndexer::new( + conf.clone(), + locator, + signer, + self.reorg_period, + )?); + Ok(indexer as Box>) + } } .context(ctx) } @@ -223,12 +251,21 @@ impl ChainConf { ) .await } - ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(conf) => { let indexer = Box::new(h_sealevel::SealevelMailboxIndexer::new(conf, locator)?); Ok(indexer as Box>) } + ChainConnectionConf::Cosmos(conf) => { + let signer = self.cosmos_signer().await.context(ctx)?; + let indexer = Box::new(h_cosmos::CosmosMailboxIndexer::new( + conf.clone(), + locator, + signer, + self.reorg_period, + )?); + Ok(indexer as Box>) + } } .context(ctx) } @@ -252,7 +289,6 @@ impl ChainConf { ) .await } - ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(conf) => { let paymaster = Box::new( @@ -260,6 +296,15 @@ impl ChainConf { ); Ok(paymaster as Box) } + ChainConnectionConf::Cosmos(conf) => { + let signer = self.cosmos_signer().await.context(ctx)?; + let paymaster = Box::new(h_cosmos::CosmosInterchainGasPaymaster::new( + conf.clone(), + locator.clone(), + signer, + )?); + Ok(paymaster as Box) + } } .context(ctx) } @@ -285,7 +330,6 @@ impl ChainConf { ) .await } - ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(conf) => { let indexer = Box::new( @@ -293,6 +337,14 @@ impl ChainConf { ); Ok(indexer as Box>) } + ChainConnectionConf::Cosmos(conf) => { + let indexer = Box::new(h_cosmos::CosmosInterchainGasPaymasterIndexer::new( + conf.clone(), + locator, + self.reorg_period, + )?); + Ok(indexer as Box>) + } } .context(ctx) } @@ -326,6 +378,17 @@ impl ChainConf { let indexer = Box::new(h_sealevel::SealevelMerkleTreeHookIndexer::new()); Ok(indexer as Box>) } + ChainConnectionConf::Cosmos(conf) => { + let signer = self.cosmos_signer().await.context(ctx)?; + let indexer = Box::new(h_cosmos::CosmosMerkleTreeHookIndexer::new( + conf.clone(), + locator, + // TODO: remove signer requirement entirely + signer, + self.reorg_period, + )?); + Ok(indexer as Box>) + } } .context(ctx) } @@ -335,18 +398,28 @@ impl ChainConf { &self, metrics: &CoreMetrics, ) -> Result> { + let ctx = "Building validator announce"; let locator = self.locator(self.addresses.validator_announce); match &self.connection { ChainConnectionConf::Ethereum(conf) => { self.build_ethereum(conf, &locator, metrics, h_eth::ValidatorAnnounceBuilder {}) .await } - ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(conf) => { let va = Box::new(h_sealevel::SealevelValidatorAnnounce::new(conf, locator)); Ok(va as Box) } + ChainConnectionConf::Cosmos(conf) => { + let signer = self.cosmos_signer().await.context(ctx)?; + let va = Box::new(h_cosmos::CosmosValidatorAnnounce::new( + conf.clone(), + locator.clone(), + signer, + )?); + + Ok(va as Box) + } } .context("Building ValidatorAnnounce") } @@ -371,7 +444,6 @@ impl ChainConf { ) .await } - ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(conf) => { let keypair = self.sealevel_signer().await.context(ctx)?; @@ -380,6 +452,13 @@ impl ChainConf { )); Ok(ism as Box) } + ChainConnectionConf::Cosmos(conf) => { + let signer = self.cosmos_signer().await.context(ctx)?; + let ism = Box::new(h_cosmos::CosmosInterchainSecurityModule::new( + conf, locator, signer, + )?); + Ok(ism as Box) + } } .context(ctx) } @@ -405,6 +484,15 @@ impl ChainConf { let ism = Box::new(h_sealevel::SealevelMultisigIsm::new(conf, locator, keypair)); Ok(ism as Box) } + ChainConnectionConf::Cosmos(conf) => { + let signer = self.cosmos_signer().await.context(ctx)?; + let ism = Box::new(h_cosmos::CosmosMultisigIsm::new( + conf.clone(), + locator.clone(), + signer, + )?); + Ok(ism as Box) + } } .context(ctx) } @@ -426,11 +514,19 @@ impl ChainConf { self.build_ethereum(conf, &locator, metrics, h_eth::RoutingIsmBuilder {}) .await } - ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(_) => { Err(eyre!("Sealevel does not support routing ISM yet")).context(ctx) } + ChainConnectionConf::Cosmos(conf) => { + let signer = self.cosmos_signer().await.context(ctx)?; + let ism = Box::new(h_cosmos::CosmosRoutingIsm::new( + &conf.clone(), + locator.clone(), + signer, + )?); + Ok(ism as Box) + } } .context(ctx) } @@ -452,11 +548,20 @@ impl ChainConf { self.build_ethereum(conf, &locator, metrics, h_eth::AggregationIsmBuilder {}) .await } - ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(_) => { Err(eyre!("Sealevel does not support aggregation ISM yet")).context(ctx) } + ChainConnectionConf::Cosmos(conf) => { + let signer = self.cosmos_signer().await.context(ctx)?; + let ism = Box::new(h_cosmos::CosmosAggregationIsm::new( + conf.clone(), + locator.clone(), + signer, + )?); + + Ok(ism as Box) + } } .context(ctx) } @@ -478,11 +583,13 @@ impl ChainConf { self.build_ethereum(conf, &locator, metrics, h_eth::CcipReadIsmBuilder {}) .await } - ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(_) => { Err(eyre!("Sealevel does not support CCIP read ISM yet")).context(ctx) } + ChainConnectionConf::Cosmos(_) => { + Err(eyre!("Cosmos does not support CCIP read ISM yet")).context(ctx) + } } .context(ctx) } @@ -495,6 +602,25 @@ impl ChainConf { } } + /// Returns a ChainSigner for the flavor of chain this is, if one is configured. + pub async fn chain_signer(&self) -> Result>> { + if let Some(conf) = &self.signer { + let chain_signer: Box = match &self.connection { + ChainConnectionConf::Ethereum(_) => Box::new(conf.build::().await?), + ChainConnectionConf::Fuel(_) => { + Box::new(conf.build::().await?) + } + ChainConnectionConf::Sealevel(_) => { + Box::new(conf.build::().await?) + } + ChainConnectionConf::Cosmos(_) => Box::new(conf.build::().await?), + }; + Ok(Some(chain_signer)) + } else { + Ok(None) + } + } + async fn ethereum_signer(&self) -> Result> { self.signer().await } @@ -509,6 +635,10 @@ impl ChainConf { self.signer().await } + async fn cosmos_signer(&self) -> Result> { + self.signer().await + } + /// Get a clone of the ethereum metrics conf with correctly configured /// contract information. fn metrics_conf( diff --git a/rust/hyperlane-base/src/settings/loader/arguments.rs b/rust/hyperlane-base/src/settings/loader/arguments.rs index eedbb476de..0f72d68eb4 100644 --- a/rust/hyperlane-base/src/settings/loader/arguments.rs +++ b/rust/hyperlane-base/src/settings/loader/arguments.rs @@ -155,7 +155,7 @@ impl Iterator for ArgumentParser { impl ArgumentParser { #[inline(never)] fn find_next_kv_pair(&mut self) -> Result, Error> { - unwrap_or_none_result!(idx, self.index_of_next_key()); + let idx = unwrap_or_none_result!(self.index_of_next_key()); // full term without leading '--' let term = &os_to_str(&self.0[idx])?[2..]; if term.is_empty() { diff --git a/rust/hyperlane-base/src/settings/mod.rs b/rust/hyperlane-base/src/settings/mod.rs index a46a467106..b999b8a323 100644 --- a/rust/hyperlane-base/src/settings/mod.rs +++ b/rust/hyperlane-base/src/settings/mod.rs @@ -73,6 +73,7 @@ pub use signers::*; pub use trace::*; mod envs { + pub use hyperlane_cosmos as h_cosmos; pub use hyperlane_ethereum as h_eth; pub use hyperlane_fuel as h_fuel; pub use hyperlane_sealevel as h_sealevel; diff --git a/rust/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/hyperlane-base/src/settings/parser/connection_parser.rs new file mode 100644 index 0000000000..b7a0a1244d --- /dev/null +++ b/rust/hyperlane-base/src/settings/parser/connection_parser.rs @@ -0,0 +1,131 @@ +use eyre::eyre; +use hyperlane_core::config::ConfigErrResultExt; +use hyperlane_core::{config::ConfigParsingError, HyperlaneDomainProtocol}; +use url::Url; + +use crate::settings::envs::*; +use crate::settings::ChainConnectionConf; + +use super::ValueParser; + +pub fn build_ethereum_connection_conf( + rpcs: &[Url], + chain: &ValueParser, + err: &mut ConfigParsingError, + default_rpc_consensus_type: &str, +) -> Option { + let Some(first_url) = rpcs.to_owned().clone().into_iter().next() else { + return None; + }; + let rpc_consensus_type = chain + .chain(err) + .get_opt_key("rpcConsensusType") + .parse_string() + .unwrap_or(default_rpc_consensus_type); + + match rpc_consensus_type { + "single" => Some(h_eth::ConnectionConf::Http { url: first_url }), + "fallback" => Some(h_eth::ConnectionConf::HttpFallback { + urls: rpcs.to_owned().clone(), + }), + "quorum" => Some(h_eth::ConnectionConf::HttpQuorum { + urls: rpcs.to_owned().clone(), + }), + ty => Err(eyre!("unknown rpc consensus type `{ty}`")) + .take_err(err, || &chain.cwp + "rpc_consensus_type"), + } + .map(ChainConnectionConf::Ethereum) +} + +pub fn build_cosmos_connection_conf( + rpcs: &[Url], + chain: &ValueParser, + err: &mut ConfigParsingError, +) -> Option { + let mut local_err = ConfigParsingError::default(); + + let grpc_url = chain + .chain(&mut local_err) + .get_key("grpcUrl") + .parse_string() + .end() + .or_else(|| { + local_err.push( + &chain.cwp + "grpc_url", + eyre!("Missing grpc definitions for chain"), + ); + None + }); + + let chain_id = chain + .chain(&mut local_err) + .get_key("chainId") + .parse_string() + .end() + .or_else(|| { + local_err.push(&chain.cwp + "chain_id", eyre!("Missing chain id for chain")); + None + }); + + let prefix = chain + .chain(err) + .get_key("prefix") + .parse_string() + .end() + .or_else(|| { + local_err.push(&chain.cwp + "prefix", eyre!("Missing prefix for chain")); + None + }); + + let canonical_asset = if let Some(asset) = chain + .chain(err) + .get_opt_key("canonicalAsset") + .parse_string() + .end() + { + Some(asset.to_string()) + } else if let Some(hrp) = prefix { + Some(format!("u{}", hrp)) + } else { + local_err.push( + &chain.cwp + "canonical_asset", + eyre!("Missing canonical asset for chain"), + ); + None + }; + + if !local_err.is_ok() { + err.merge(local_err); + None + } else { + Some(ChainConnectionConf::Cosmos(h_cosmos::ConnectionConf::new( + grpc_url.unwrap().to_string(), + rpcs.first().unwrap().to_string(), + chain_id.unwrap().to_string(), + prefix.unwrap().to_string(), + canonical_asset.unwrap(), + ))) + } +} + +pub fn build_connection_conf( + domain_protocol: HyperlaneDomainProtocol, + rpcs: &[Url], + chain: &ValueParser, + err: &mut ConfigParsingError, + default_rpc_consensus_type: &str, +) -> Option { + match domain_protocol { + HyperlaneDomainProtocol::Ethereum => { + build_ethereum_connection_conf(rpcs, chain, err, default_rpc_consensus_type) + } + HyperlaneDomainProtocol::Fuel => rpcs + .iter() + .next() + .map(|url| ChainConnectionConf::Fuel(h_fuel::ConnectionConf { url: url.clone() })), + HyperlaneDomainProtocol::Sealevel => rpcs.iter().next().map(|url| { + ChainConnectionConf::Sealevel(h_sealevel::ConnectionConf { url: url.clone() }) + }), + HyperlaneDomainProtocol::Cosmos => build_cosmos_connection_conf(rpcs, chain, err), + } +} diff --git a/rust/hyperlane-base/src/settings/parser/mod.rs b/rust/hyperlane-base/src/settings/parser/mod.rs index cbfe3b2fc3..76ed1d5944 100644 --- a/rust/hyperlane-base/src/settings/parser/mod.rs +++ b/rust/hyperlane-base/src/settings/parser/mod.rs @@ -21,10 +21,11 @@ use serde_json::Value; pub use self::json_value_parser::ValueParser; pub use super::envs::*; use crate::settings::{ - chains::IndexSettings, trace::TracingConfig, ChainConf, ChainConnectionConf, - CoreContractAddresses, Settings, SignerConf, + chains::IndexSettings, parser::connection_parser::build_connection_conf, trace::TracingConfig, + ChainConf, CoreContractAddresses, Settings, SignerConf, }; +mod connection_parser; mod json_value_parser; /// The base agent config @@ -224,40 +225,13 @@ fn parse_chain( .end(); cfg_unwrap_all!(&chain.cwp, err: [domain]); - - let connection: Option = match domain.domain_protocol() { - HyperlaneDomainProtocol::Ethereum => { - if rpcs.len() <= 1 { - rpcs.into_iter() - .next() - .map(|url| ChainConnectionConf::Ethereum(h_eth::ConnectionConf::Http { url })) - } else { - let rpc_consensus_type = chain - .chain(&mut err) - .get_opt_key("rpcConsensusType") - .parse_string() - .unwrap_or(default_rpc_consensus_type); - match rpc_consensus_type { - "single" => Some(h_eth::ConnectionConf::Http { - url: rpcs.into_iter().next().unwrap(), - }), - "fallback" => Some(h_eth::ConnectionConf::HttpFallback { urls: rpcs }), - "quorum" => Some(h_eth::ConnectionConf::HttpQuorum { urls: rpcs }), - ty => Err(eyre!("unknown rpc consensus type `{ty}`")) - .take_err(&mut err, || &chain.cwp + "rpc_consensus_type"), - } - .map(ChainConnectionConf::Ethereum) - } - } - HyperlaneDomainProtocol::Fuel => rpcs - .into_iter() - .next() - .map(|url| ChainConnectionConf::Fuel(h_fuel::ConnectionConf { url })), - HyperlaneDomainProtocol::Sealevel => rpcs - .into_iter() - .next() - .map(|url| ChainConnectionConf::Sealevel(h_sealevel::ConnectionConf { url })), - }; + let connection = build_connection_conf( + domain.domain_protocol(), + &rpcs, + &chain, + &mut err, + default_rpc_consensus_type, + ); cfg_unwrap_all!(&chain.cwp, err: [connection, mailbox, interchain_gas_paymaster, validator_announce]); err.into_result(ChainConf { @@ -327,7 +301,7 @@ fn parse_signer(signer: ValueParser) -> ConfigResult { let signer_type = signer .chain(&mut err) - .get_opt_key("signerType") + .get_opt_key("type") .parse_string() .end(); @@ -358,11 +332,28 @@ fn parse_signer(signer: ValueParser) -> ConfigResult { .unwrap_or_default(); err.into_result(SignerConf::Aws { id, region }) }}; + (cosmosKey) => {{ + let key = signer + .chain(&mut err) + .get_key("key") + .parse_private_key() + .unwrap_or_default(); + let prefix = signer + .chain(&mut err) + .get_key("prefix") + .parse_string() + .unwrap_or_default(); + err.into_result(SignerConf::CosmosKey { + key, + prefix: prefix.to_string(), + }) + }}; } match signer_type { Some("hexKey") => parse_signer!(hexKey), Some("aws") => parse_signer!(aws), + Some("cosmosKey") => parse_signer!(cosmosKey), Some(t) => { Err(eyre!("Unknown signer type `{t}`")).into_config_result(|| &signer.cwp + "type") } diff --git a/rust/hyperlane-base/src/settings/signers.rs b/rust/hyperlane-base/src/settings/signers.rs index df7f69ae90..2979488544 100644 --- a/rust/hyperlane-base/src/settings/signers.rs +++ b/rust/hyperlane-base/src/settings/signers.rs @@ -28,7 +28,14 @@ pub enum SignerConf { /// The AWS region region: Region, }, - /// Assume the local node will sign on RPC calls automatically + /// Cosmos Specific key + CosmosKey { + /// Private key value + key: H256, + /// Prefix for cosmos address + prefix: String, + }, + /// Assume node will sign on RPC calls #[default] Node, } @@ -41,9 +48,15 @@ impl SignerConf { } } +/// A signer for a chain. +pub trait ChainSigner: Send { + /// The address of the signer, formatted in the chain's own address format. + fn address_string(&self) -> String; +} + /// Builder trait for signers #[async_trait] -pub trait BuildableWithSignerConf: Sized { +pub trait BuildableWithSignerConf: Sized + ChainSigner { /// Build a signer from a conf async fn build(conf: &SignerConf) -> Result; } @@ -73,38 +86,79 @@ impl BuildableWithSignerConf for hyperlane_ethereum::Signers { let signer = AwsSigner::new(client, id, 0).await?; hyperlane_ethereum::Signers::Aws(signer) } + SignerConf::CosmosKey { .. } => { + bail!("cosmosKey signer is not supported by Ethereum") + } SignerConf::Node => bail!("Node signer"), }) } } +impl ChainSigner for hyperlane_ethereum::Signers { + fn address_string(&self) -> String { + ethers::abi::AbiEncode::encode_hex(ethers::signers::Signer::address(self)) + } +} + #[async_trait] impl BuildableWithSignerConf for fuels::prelude::WalletUnlocked { async fn build(conf: &SignerConf) -> Result { - Ok(match conf { - SignerConf::HexKey { key } => { - let key = fuels::signers::fuel_crypto::SecretKey::try_from(key.as_bytes()) - .context("Invalid fuel signer key")?; - fuels::prelude::WalletUnlocked::new_from_private_key(key, None) - } - SignerConf::Aws { .. } => bail!("Aws signer is not supported by fuel"), - SignerConf::Node => bail!("Node signer is not supported by fuel"), - }) + if let SignerConf::HexKey { key } = conf { + let key = fuels::signers::fuel_crypto::SecretKey::try_from(key.as_bytes()) + .context("Invalid fuel signer key")?; + Ok(fuels::prelude::WalletUnlocked::new_from_private_key( + key, None, + )) + } else { + bail!(format!("{conf:?} key is not supported by fuel")); + } + } +} + +impl ChainSigner for fuels::prelude::WalletUnlocked { + fn address_string(&self) -> String { + self.address().to_string() } } #[async_trait] impl BuildableWithSignerConf for Keypair { async fn build(conf: &SignerConf) -> Result { - Ok(match conf { - SignerConf::HexKey { key } => { - let secret = SecretKey::from_bytes(key.as_bytes()) - .context("Invalid sealevel ed25519 secret key")?; + if let SignerConf::HexKey { key } = conf { + let secret = SecretKey::from_bytes(key.as_bytes()) + .context("Invalid sealevel ed25519 secret key")?; + Ok( Keypair::from_bytes(&ed25519_dalek::Keypair::from(secret).to_bytes()) - .context("Unable to create Keypair")? - } - SignerConf::Aws { .. } => bail!("Aws signer is not supported by fuel"), - SignerConf::Node => bail!("Node signer is not supported by fuel"), - }) + .context("Unable to create Keypair")?, + ) + } else { + bail!(format!("{conf:?} key is not supported by sealevel")); + } + } +} + +impl ChainSigner for Keypair { + fn address_string(&self) -> String { + solana_sdk::signer::Signer::pubkey(self).to_string() + } +} + +#[async_trait] +impl BuildableWithSignerConf for hyperlane_cosmos::Signer { + async fn build(conf: &SignerConf) -> Result { + if let SignerConf::CosmosKey { key, prefix } = conf { + Ok(hyperlane_cosmos::Signer::new( + key.as_bytes().to_vec(), + prefix.clone(), + )?) + } else { + bail!(format!("{conf:?} key is not supported by cosmos")); + } + } +} + +impl ChainSigner for hyperlane_cosmos::Signer { + fn address_string(&self) -> String { + self.address.clone() } } diff --git a/rust/hyperlane-base/src/types/multisig.rs b/rust/hyperlane-base/src/types/multisig.rs index 0b1ff5c4dd..4fd60f7985 100644 --- a/rust/hyperlane-base/src/types/multisig.rs +++ b/rust/hyperlane-base/src/types/multisig.rs @@ -123,8 +123,10 @@ impl MultisigCheckpointSyncer { ); continue; } - // Ensure that the signature is actually by the validator at the current index + + // Ensure that the signature is actually by the validator let signer = signed_checkpoint.recover()?; + if H256::from(signer) != *validator { debug!( validator = format!("{:#x}", validator), diff --git a/rust/hyperlane-core/Cargo.toml b/rust/hyperlane-core/Cargo.toml index 6489974004..bbb0835b8c 100644 --- a/rust/hyperlane-core/Cargo.toml +++ b/rust/hyperlane-core/Cargo.toml @@ -2,12 +2,12 @@ cargo-features = ["workspace-inheritance"] [package] name = "hyperlane-core" -documentation.workspace = true -edition.workspace = true -homepage.workspace = true -license-file.workspace = true -publish.workspace = true -version.workspace = true +documentation = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license-file = { workspace = true } +publish = { workspace = true } +version = { workspace = true } [dependencies] async-trait.workspace = true @@ -30,13 +30,13 @@ itertools.workspace = true num = { workspace = true, features = ["serde"] } num-derive.workspace = true num-traits.workspace = true +serde = { workspace = true } +serde_json = { workspace = true } +sha3 = { workspace = true } +strum = { workspace = true, optional = true, features = ["derive"] } +thiserror = { workspace = true } primitive-types = { workspace = true, optional = true } -serde.workspace = true -serde_json.workspace = true -sha3.workspace = true solana-sdk = { workspace = true, optional = true } -strum = { workspace = true, optional = true, features = ["derive"] } -thiserror.workspace = true tiny-keccak = { workspace = true, features = ["keccak"]} uint.workspace = true diff --git a/rust/hyperlane-core/src/accumulator/incremental.rs b/rust/hyperlane-core/src/accumulator/incremental.rs index f967b452d4..0c265202e8 100644 --- a/rust/hyperlane-core/src/accumulator/incremental.rs +++ b/rust/hyperlane-core/src/accumulator/incremental.rs @@ -10,8 +10,10 @@ use crate::accumulator::{ #[derive(BorshDeserialize, BorshSerialize, Debug, Clone, Copy, new, PartialEq, Eq)] /// An incremental merkle tree, modeled on the eth2 deposit contract pub struct IncrementalMerkle { - branch: [H256; TREE_DEPTH], - count: usize, + /// The branch of the tree + pub branch: [H256; TREE_DEPTH], + /// The number of leaves in the tree + pub count: usize, } impl Default for IncrementalMerkle { diff --git a/rust/hyperlane-core/src/chain.rs b/rust/hyperlane-core/src/chain.rs index c727bfb746..94c2c5c44e 100644 --- a/rust/hyperlane-core/src/chain.rs +++ b/rust/hyperlane-core/src/chain.rs @@ -99,6 +99,10 @@ pub enum KnownHyperlaneDomain { LineaGoerli = 59140, BaseGoerli = 84531, ScrollSepolia = 534351, + + /// Cosmos local chains + CosmosTest26657 = 26657, + CosmosTest26658 = 26658, } #[derive(Clone)] @@ -162,6 +166,8 @@ pub enum HyperlaneDomainProtocol { Fuel, /// A Sealevel-based chain type which uses hyperlane-sealevel. Sealevel, + /// A Cosmos-based chain type which uses hyperlane-cosmos. + Cosmos, } impl HyperlaneDomainProtocol { @@ -171,6 +177,7 @@ impl HyperlaneDomainProtocol { Ethereum => format!("{:?}", H160::from(addr)), Fuel => format!("{:?}", addr), Sealevel => format!("{:?}", addr), + Cosmos => format!("{:?}", addr), } } } @@ -193,7 +200,7 @@ impl KnownHyperlaneDomain { Goerli, Mumbai, Fuji, ArbitrumGoerli, OptimismGoerli, BinanceSmartChainTestnet, Alfajores, MoonbaseAlpha, Sepolia, PolygonZkEvmTestnet, LineaGoerli, BaseGoerli, ScrollSepolia, Chiado ], - LocalTestChain: [Test1, Test2, Test3, FuelTest1, SealevelTest1, SealevelTest2], + LocalTestChain: [Test1, Test2, Test3, FuelTest1, SealevelTest1, SealevelTest2, CosmosTest26657, CosmosTest26658], }) } @@ -208,6 +215,7 @@ impl KnownHyperlaneDomain { ], HyperlaneDomainProtocol::Fuel: [FuelTest1], HyperlaneDomainProtocol::Sealevel: [SealevelTest1, SealevelTest2], + HyperlaneDomainProtocol::Cosmos: [CosmosTest26657, CosmosTest26658], }) } } @@ -296,7 +304,7 @@ impl HyperlaneDomain { ) -> Result { let name = name.to_ascii_lowercase(); if let Ok(domain) = KnownHyperlaneDomain::try_from(domain_id) { - if name == domain.as_str() { + if name == domain.as_str().to_ascii_lowercase() { Ok(HyperlaneDomain::Known(domain)) } else { Err(HyperlaneDomainConfigError::UnknownDomainName(name)) @@ -367,7 +375,7 @@ impl HyperlaneDomain { use HyperlaneDomainProtocol::*; let protocol = self.domain_protocol(); many_to_one!(match protocol { - IndexMode::Block: [Ethereum], + IndexMode::Block: [Ethereum, Cosmos], // TODO: Is cosmos index-mode is correct? IndexMode::Sequence : [Sealevel, Fuel], }) } diff --git a/rust/hyperlane-core/src/error.rs b/rust/hyperlane-core/src/error.rs index 3be9490610..ae7c5f8b54 100644 --- a/rust/hyperlane-core/src/error.rs +++ b/rust/hyperlane-core/src/error.rs @@ -4,6 +4,9 @@ use std::fmt::{Debug, Display, Formatter}; use std::ops::Deref; use crate::config::StrOrIntParseError; +use std::string::FromUtf8Error; + +use crate::Error as PrimitiveTypeError; use crate::HyperlaneProviderError; use crate::H256; @@ -78,6 +81,48 @@ pub enum ChainCommunicationError { /// Failed to parse strings or integers #[error("Data parsing error {0:?}")] StrOrIntParseError(#[from] StrOrIntParseError), + /// BlockNotFoundError + #[error("Block not found: {0:?}")] + BlockNotFound(H256), + /// utf8 error + #[error("{0}")] + Utf8(#[from] FromUtf8Error), + /// Serde JSON error + #[error("{0}")] + JsonParseError(#[from] serde_json::Error), + /// String hex parsing error + #[error("{0}")] + HexParseError(#[from] hex::FromHexError), + /// Uint hex parsing error + #[error("{0}")] + UintParseError(#[from] uint::FromHexError), + /// Decimal string parsing error + #[error("{0}")] + FromDecStrError(#[from] uint::FromDecStrErr), + /// Int string parsing error + #[error("{0}")] + ParseIntError(#[from] std::num::ParseIntError), + /// Hash string parsing error + #[error("{0}")] + HashParsingError(#[from] fixed_hash::rustc_hex::FromHexError), + /// Invalid Request + #[error("Invalid Request: {msg:?}")] + InvalidRequest { + /// Error message + msg: String, + }, + /// Parse Error + #[error("ParseError: {msg:?}")] + ParseError { + /// Error message + msg: String, + }, + /// Failed to estimate transaction gas cost. + #[error("Failed to estimate transaction gas cost {0}")] + TxCostEstimateError(String), + /// Primitive type error + #[error(transparent)] + PrimitiveTypeError(#[from] PrimitiveTypeError), } impl ChainCommunicationError { diff --git a/rust/hyperlane-core/src/traits/cursor.rs b/rust/hyperlane-core/src/traits/cursor.rs index 68d2c8b7d4..4deb063fde 100644 --- a/rust/hyperlane-core/src/traits/cursor.rs +++ b/rust/hyperlane-core/src/traits/cursor.rs @@ -1,4 +1,4 @@ -use std::{ops::RangeInclusive, time::Duration}; +use std::{fmt, ops::RangeInclusive, time::Duration}; use async_trait::async_trait; use auto_impl::auto_impl; @@ -27,3 +27,12 @@ pub enum CursorAction { /// Direct the contract_sync task to sleep for a duration Sleep(Duration), } + +impl fmt::Debug for CursorAction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CursorAction::Query(range) => write!(f, "Query({:?})", range), + CursorAction::Sleep(duration) => write!(f, "Sleep({:?})", duration), + } + } +} diff --git a/rust/hyperlane-core/src/traits/db.rs b/rust/hyperlane-core/src/traits/db.rs index 50f9960e04..3fbebf52db 100644 --- a/rust/hyperlane-core/src/traits/db.rs +++ b/rust/hyperlane-core/src/traits/db.rs @@ -4,7 +4,7 @@ use async_trait::async_trait; use auto_impl::auto_impl; use eyre::Result; -use crate::{HyperlaneMessage, LogMeta}; +use crate::LogMeta; /// Interface for a HyperlaneLogStore that ingests logs. #[async_trait] @@ -15,14 +15,26 @@ pub trait HyperlaneLogStore: Send + Sync + Debug { async fn store_logs(&self, logs: &[(T, LogMeta)]) -> Result; } -/// Extension of HyperlaneLogStore trait that supports getting the block number at which a known message was dispatched. +/// A sequence is a monotonically increasing number that is incremented every time a message ID is indexed. +/// E.g. for Mailbox indexing, this is equal to the message nonce, and for merkle tree hook indexing, this +/// is equal to the leaf index. +pub trait Sequenced: 'static + Send + Sync { + /// The sequence of this sequenced type. + fn sequence(&self) -> u32; +} + +/// Extension of HyperlaneLogStore trait that supports indexed sequenced data. #[async_trait] #[auto_impl(&, Box, Arc)] -pub trait HyperlaneMessageStore: HyperlaneLogStore { - /// Gets a message by nonce. - async fn retrieve_message_by_nonce(&self, nonce: u32) -> Result>; - /// Gets the block number at which a message was dispatched. - async fn retrieve_dispatched_block_number(&self, nonce: u32) -> Result>; +pub trait HyperlaneSequenceIndexerStore: HyperlaneLogStore +where + T: Send + Sync, +{ + /// Gets data by its sequence. + async fn retrieve_by_sequence(&self, sequence: u32) -> Result>; + + /// Gets the block number at which the log occurred. + async fn retrieve_log_block_number(&self, nonce: u32) -> Result>; } /// Extension of HyperlaneLogStore trait that supports a high watermark for the highest indexed block number. @@ -31,6 +43,7 @@ pub trait HyperlaneMessageStore: HyperlaneLogStore { pub trait HyperlaneWatermarkedLogStore: HyperlaneLogStore { /// Gets the block number high watermark async fn retrieve_high_watermark(&self) -> Result>; + /// Stores the block number high watermark async fn store_high_watermark(&self, block_number: u32) -> Result<()>; } diff --git a/rust/hyperlane-core/src/traits/signing.rs b/rust/hyperlane-core/src/traits/signing.rs index 5257279192..9fe8c89151 100644 --- a/rust/hyperlane-core/src/traits/signing.rs +++ b/rust/hyperlane-core/src/traits/signing.rs @@ -1,11 +1,10 @@ -use std::fmt::{Debug, Formatter}; - use async_trait::async_trait; use auto_impl::auto_impl; use serde::{ ser::{SerializeStruct, Serializer}, Deserialize, Serialize, }; +use std::fmt::{Debug, Formatter}; use crate::utils::fmt_bytes; use crate::{Signature, H160, H256}; @@ -53,6 +52,7 @@ impl HyperlaneSignerExt for S { ) -> Result, HyperlaneSignerError> { let signing_hash = value.signing_hash(); let signature = self.sign_hash(&signing_hash).await?; + Ok(SignedType { value, signature }) } @@ -110,6 +110,7 @@ impl SignedType { pub fn recover(&self) -> Result { let hash = ethers_core::types::H256::from(self.value.eth_signed_message_hash()); let sig = ethers_core::types::Signature::from(self.signature); + Ok(sig.recover(hash)?.into()) } @@ -153,7 +154,6 @@ mod hashes { let mut eth_message = format!("{PREFIX}{}", message.len()).into_bytes(); eth_message.extend_from_slice(message); - keccak256(ð_message).into() } diff --git a/rust/hyperlane-core/src/types/merkle_tree.rs b/rust/hyperlane-core/src/types/merkle_tree.rs index 1a5a4058f0..96ea78ff1c 100644 --- a/rust/hyperlane-core/src/types/merkle_tree.rs +++ b/rust/hyperlane-core/src/types/merkle_tree.rs @@ -1,10 +1,10 @@ use derive_new::new; use std::io::{Read, Write}; -use crate::{Decode, Encode, HyperlaneProtocolError, H256}; +use crate::{Decode, Encode, HyperlaneProtocolError, Sequenced, H256}; /// Merkle Tree Hook insertion event -#[derive(Debug, Copy, Clone, new)] +#[derive(Debug, Copy, Clone, new, Eq, PartialEq)] pub struct MerkleTreeInsertion { leaf_index: u32, message_id: H256, @@ -22,6 +22,12 @@ impl MerkleTreeInsertion { } } +impl Sequenced for MerkleTreeInsertion { + fn sequence(&self) -> u32 { + self.leaf_index + } +} + impl Encode for MerkleTreeInsertion { fn write_to(&self, writer: &mut W) -> std::io::Result where diff --git a/rust/hyperlane-core/src/types/message.rs b/rust/hyperlane-core/src/types/message.rs index 1faec8436d..0291d33fb2 100644 --- a/rust/hyperlane-core/src/types/message.rs +++ b/rust/hyperlane-core/src/types/message.rs @@ -2,7 +2,7 @@ use sha3::{digest::Update, Digest, Keccak256}; use std::fmt::{Debug, Display, Formatter}; use crate::utils::{fmt_address_for_domain, fmt_domain}; -use crate::{Decode, Encode, HyperlaneProtocolError, H256}; +use crate::{Decode, Encode, HyperlaneProtocolError, Sequenced, H256}; const HYPERLANE_MESSAGE_PREFIX_LEN: usize = 77; @@ -21,7 +21,7 @@ impl From<&HyperlaneMessage> for RawHyperlaneMessage { } /// A full Hyperlane message between chains -#[derive(Default, Clone)] +#[derive(Default, Clone, Eq, PartialEq)] pub struct HyperlaneMessage { /// 1 Hyperlane version number pub version: u8, @@ -39,6 +39,12 @@ pub struct HyperlaneMessage { pub body: Vec, } +impl Sequenced for HyperlaneMessage { + fn sequence(&self) -> u32 { + self.nonce + } +} + impl Debug for HyperlaneMessage { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( diff --git a/rust/hyperlane-core/src/types/mod.rs b/rust/hyperlane-core/src/types/mod.rs index 4ef1f4411b..c1e39e491b 100644 --- a/rust/hyperlane-core/src/types/mod.rs +++ b/rust/hyperlane-core/src/types/mod.rs @@ -115,7 +115,7 @@ pub struct GasPaymentKey { } /// A payment of a message's gas costs. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)] pub struct InterchainGasPayment { /// Id of the message pub message_id: H256, diff --git a/rust/hyperlane-core/src/types/primitive_types.rs b/rust/hyperlane-core/src/types/primitive_types.rs index 58dd9bffab..2b0f5112e6 100644 --- a/rust/hyperlane-core/src/types/primitive_types.rs +++ b/rust/hyperlane-core/src/types/primitive_types.rs @@ -10,9 +10,10 @@ use uint::construct_uint; use crate::types::serialize; /// Error type for conversion. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, thiserror::Error)] pub enum Error { /// Overflow encountered. + #[error("Overflow when creating primitive type")] Overflow, } diff --git a/rust/hyperlane-core/src/utils.rs b/rust/hyperlane-core/src/utils.rs index 9d8b8b0017..56a3e4c9b3 100644 --- a/rust/hyperlane-core/src/utils.rs +++ b/rust/hyperlane-core/src/utils.rs @@ -1,7 +1,9 @@ -use std::{str::FromStr, time::Duration}; - use eyre::Result; use sha3::{digest::Update, Digest, Keccak256}; +use std::str::FromStr; + +#[cfg(feature = "float")] +use std::time::Duration; use crate::{KnownHyperlaneDomain, H160, H256}; @@ -238,13 +240,14 @@ macro_rules! many_to_one { /// return Ok(None); /// }; /// // after: -/// unwrap_or_none_result!(idx, self.index_of_next_key()); +/// let idx = unwrap_or_none_result!(self.index_of_next_key()); /// ``` #[macro_export] macro_rules! unwrap_or_none_result { - ($variable_name:ident, $e:expr $(, $else_e:expr)?) => { - let Some($variable_name) = $e - else { + ($e:expr $(, $else_e:expr)?) => { + if let Some(inner) = $e { + inner + } else { $($else_e;)? return Ok(None); }; diff --git a/rust/utils/run-locally/Cargo.toml b/rust/utils/run-locally/Cargo.toml index 335c58cedc..1f59fb2fb3 100644 --- a/rust/utils/run-locally/Cargo.toml +++ b/rust/utils/run-locally/Cargo.toml @@ -10,6 +10,14 @@ publish.workspace = true version.workspace = true [dependencies] +hyperlane-core = { path = "../../hyperlane-core" } +toml_edit.workspace = true +k256.workspace = true +ripemd.workspace = true +sha2.workspace = true +serde.workspace = true +serde_json.workspace = true +hex.workspace = true ctrlc.workspace = true eyre.workspace = true maplit.workspace = true @@ -19,4 +27,5 @@ ureq = { workspace = true, default-features = false } which.workspace = true macro_rules_attribute.workspace = true regex.workspace = true -hyperlane-core = { path = "../../hyperlane-core" } +hpl-interface.workspace = true +cosmwasm-schema.workspace = true diff --git a/rust/utils/run-locally/src/cosmos/cli.rs b/rust/utils/run-locally/src/cosmos/cli.rs new file mode 100644 index 0000000000..b221c9f499 --- /dev/null +++ b/rust/utils/run-locally/src/cosmos/cli.rs @@ -0,0 +1,434 @@ +use std::{collections::BTreeMap, io::Write, path::PathBuf, process::Stdio}; + +use k256::ecdsa::SigningKey; + +use crate::{ + program::Program, + utils::{concat_path, AgentHandles, TaskHandle}, +}; + +use super::{ + crypto::KeyPair, default_keys, modify_toml, sed, types::BalanceResponse, wait_for_node, Codes, + Coin, TxResponse, +}; + +const GENESIS_FUND: u128 = 1000000000000; + +#[derive(Clone)] +pub struct OsmosisEndpoint { + pub addr: String, + pub rpc_addr: String, + pub grpc_addr: String, +} + +impl OsmosisEndpoint { + fn wait_for_node(&self) { + wait_for_node(&self.rpc_addr) + } + + fn add_rpc(&self, program: Program) -> Program { + program.arg("node", &self.rpc_addr) + } +} + +pub struct OsmosisCLI { + pub bin: PathBuf, + pub home: String, +} + +#[allow(dead_code)] +impl OsmosisCLI { + pub fn new(bin: PathBuf, home: &str) -> Self { + Self { + bin, + home: home.to_string(), + } + } + + fn cli(&self) -> Program { + Program::new(self.bin.clone()).arg("home", &self.home) + } + + fn add_gas(&self, program: Program) -> Program { + program + .arg("gas", "auto") + .arg("gas-prices", "0.025uosmo") + .arg("gas-adjustment", "1.5") + .flag("yes") + } + + pub fn init(&self, moniker: &str, chain_id: &str) { + self.cli() + .cmd("init") + .cmd(moniker) + .arg("chain-id", chain_id) + .run() + .join(); + + let genesis_path = concat_path(&self.home, "config/genesis.json"); + sed("stake", "uosmo", genesis_path.to_str().unwrap()); + + // modify node config + let node_config_path = concat_path(&self.home, "config/config.toml"); + modify_toml( + node_config_path, + Box::new(|v| { + v["p2p"]["pex"] = toml_edit::value(false); + v["consensus"]["timeout_commit"] = toml_edit::value("0.5s"); + }), + ); + + // modify app config + let app_config_path = concat_path(&self.home, "config/app.toml"); + modify_toml( + app_config_path, + Box::new(|v| { + v["minimum-gas-prices"] = toml_edit::value("0.025uosmo"); + v["pruning"] = toml_edit::value("nothing"); // archive + v["api"]["enable"] = toml_edit::value(false); + v["grpc-web"]["enable"] = toml_edit::value(false); + }), + ); + + // modify client config + let client_chain_id = chain_id.to_string(); + let client_config_path = concat_path(&self.home, "config/client.toml"); + modify_toml( + client_config_path, + Box::new(move |v| { + v["keyring-backend"] = toml_edit::value("test"); + v["output"] = toml_edit::value("json"); + v["chain-id"] = toml_edit::value(client_chain_id.clone()); + v["broadcast-mode"] = toml_edit::value("block"); + }), + ); + + self.add_default_keys(); + self.add_genesis_accs(); + + self.cli() + .cmd("gentx") + .cmd("validator") + .cmd(format!("{}uosmo", GENESIS_FUND)) + .arg("chain-id", chain_id) + .run() + .join(); + + self.cli().cmd("collect-gentxs").run().join(); + } + + pub fn start(&self, addr_base: String, port_base: u32) -> (AgentHandles, OsmosisEndpoint) { + if !addr_base.starts_with("tcp://") { + panic!("invalid addr_base: {}", addr_base); + } + + let mut next_port = port_base; + let mut get_next_addr = || { + let port = next_port; + next_port += 1; + format!("{addr_base}:{port}") + }; + + let addr = get_next_addr(); + let p2p_addr = get_next_addr(); + let rpc_addr = get_next_addr(); + let grpc_addr = get_next_addr().replace("tcp://", ""); + let pprof_addr = get_next_addr().replace("tcp://", ""); + + let endpoint = OsmosisEndpoint { + addr, + rpc_addr, + grpc_addr, + }; + + let node = self + .cli() + .cmd("start") + .arg("address", &endpoint.addr) // default is tcp://0.0.0.0:26658 + // addrs + .arg("p2p.laddr", p2p_addr) // default is tcp://0.0.0.0:26655 + .arg("rpc.laddr", &endpoint.rpc_addr) // default is tcp://0.0.0.0:26657 + .arg("grpc.address", &endpoint.grpc_addr) // default is 0.0.0.0:9090 + .arg("rpc.pprof_laddr", pprof_addr) // default is localhost:6060 + .arg("log_level", "panic") + .spawn("COSMOS"); + + endpoint.wait_for_node(); + + (node, endpoint) + } + + pub fn store_codes( + &self, + endpoint: &OsmosisEndpoint, + sender: &str, + codes: BTreeMap, + ) -> Codes { + let mut ret = BTreeMap::::new(); + + for (name, code) in codes { + let cmd = self + .cli() + .cmd("tx") + .cmd("wasm") + .cmd("store") + .cmd(code.to_str().unwrap()) + .arg("from", sender); + + let cmd = self.add_gas(cmd); + let cmd = endpoint.add_rpc(cmd); + + let raw_output = cmd.run_with_output().join(); + println!("wasm store code res: {:?}", raw_output); + + let wasm_store_tx_resp: TxResponse = + serde_json::from_str(raw_output.first().unwrap()).unwrap(); + + let store_code_log = wasm_store_tx_resp.logs.first().unwrap(); + let store_code_evt = store_code_log + .events + .iter() + .find(|v| v.typ == "store_code") + .unwrap(); + + let code_id = &store_code_evt.attributes.last().unwrap().value; + let code_id = code_id.parse::().unwrap(); + + ret.insert(name, code_id); + } + serde_json::from_str(&serde_json::to_string(&ret).unwrap()).unwrap() + } + + pub fn wasm_init( + &self, + endpoint: &OsmosisEndpoint, + sender: &str, + admin: Option<&str>, + code_id: u64, + init_msg: T, + label: &str, + ) -> String { + let mut cmd = self + .cli() + .cmd("tx") + .cmd("wasm") + .cmd("instantiate") + .cmd(code_id.to_string()) + .cmd(serde_json::to_string(&init_msg).unwrap()) + .arg("from", sender) + .arg("label", label); + + cmd = self.add_gas(cmd); + cmd = endpoint.add_rpc(cmd); + + if let Some(admin) = admin { + cmd = cmd.arg("admin", admin); + } else { + cmd = cmd.flag("no-admin"); + } + + let wasm_init_resp: TxResponse = + serde_json::from_str(cmd.run_with_output().join().first().unwrap()).unwrap(); + + let init_log = wasm_init_resp.logs.first().unwrap(); + let init_evt = init_log + .events + .iter() + .find(|v| v.typ == "instantiate") + .unwrap(); + + let contract_addr = &init_evt + .attributes + .iter() + .find(|v| v.key == "_contract_address") + .unwrap() + .value; + + contract_addr.to_string() + } + + pub fn wasm_execute( + &self, + endpoint: &OsmosisEndpoint, + sender: &str, + contract: &str, + execute_msg: T, + funds: Vec, + ) -> TxResponse { + let mut cmd = self + .cli() + .cmd("tx") + .cmd("wasm") + .cmd("execute") + .cmd(contract) + .cmd(serde_json::to_string(&execute_msg).unwrap()) + .arg("from", sender); + + cmd = self.add_gas(cmd); + cmd = endpoint.add_rpc(cmd); + + if !funds.is_empty() { + cmd = cmd.arg( + "amount", + funds + .into_iter() + .map(|v| format!("{}{}", v.amount, v.denom)) + .collect::>() + .join(","), + ); + } + + let run_result = cmd.run_with_output().join(); + + println!("wasm execute res: {:?}", run_result); + + let output: Result = + serde_json::from_str(run_result.first().unwrap()); + + output.unwrap() + } + + pub fn wasm_query( + // U: serde::de::DeserializeOwned>( + &self, + endpoint: &OsmosisEndpoint, + contract: &str, + query_msg: T, + ) { + let mut cmd = self + .cli() + .cmd("query") + .cmd("wasm") + .cmd("contract-state") + .cmd("smart") + .cmd(contract) + .cmd(serde_json::to_string(&query_msg).unwrap()); + + cmd = endpoint.add_rpc(cmd); + + let output = cmd.run_with_output().join(); + let output = output.first().unwrap(); + + println!("output: {:?}", output); + // let output: CliWasmQueryResponse = serde_json::from_str(output).unwrap(); + + // output.data + } + + pub fn query_balance(&self, endpoint: &OsmosisEndpoint, addr: &str) -> BalanceResponse { + let cmd = endpoint + .add_rpc(self.cli()) + .cmd("query") + .cmd("bank") + .cmd("balances") + .cmd(addr) + .run_with_output() + .join(); + + let output = serde_json::from_str(cmd.first().unwrap()).unwrap(); + + output + } + + pub fn bank_send( + &self, + endpoint: &OsmosisEndpoint, + sender: &str, + sender_addr: &str, + addr: &str, + funds: &str, + ) { + let mut cmd = self + .cli() + .cmd("tx") + .cmd("bank") + .cmd("send") + .cmd(sender_addr) + .cmd(addr) + .cmd(funds) + .arg("from", sender); + + cmd = self.add_gas(cmd); + cmd = endpoint.add_rpc(cmd); + + cmd.run().join(); + } + + fn add_genesis_accs(&self) { + for name in default_keys().into_iter().map(|(name, _)| name) { + self.cli() + .cmd("add-genesis-account") + .cmd(self.get_addr(name)) + .cmd(format!("{}uosmo", GENESIS_FUND * 2)) + .run() + .join(); + } + } + + fn add_default_keys(&self) { + for (name, mnemonic) in default_keys() { + self.add_key(name, mnemonic); + } + } + + pub fn add_key(&self, name: &str, mnemonic: &str) { + let mut child = self + .cli() + .cmd("keys") + .cmd("add") + .cmd(name) + .flag("recover") + .create_command() + .stdin(Stdio::piped()) + .spawn() + .expect("failed to spawn process"); + + child + .stdin + .as_mut() + .unwrap() + .write_all(format!("{mnemonic}\n").as_bytes()) + .unwrap(); + + child.wait().unwrap(); + } + + pub fn get_addr(&self, name: &str) -> String { + let out = self + .cli() + .cmd("keys") + .cmd("show") + .raw_arg("-a") + .cmd(name) + .run_with_output() + .join(); + out.first().unwrap().clone() + } + + pub fn get_keypair(&self, name: &str) -> KeyPair { + let cmd = self + .cli() + .cmd("keys") + .cmd("export") + .cmd(name) + .flag("unarmored-hex") + .flag("unsafe"); + + let mut proc = cmd + .create_command() + .stderr(Stdio::piped()) + .stdin(Stdio::piped()) + .spawn() + .unwrap(); + + proc.stdin.as_mut().unwrap().write_all(b"y\n").unwrap(); + let proc_output = proc.wait_with_output().unwrap(); + let proc_output_str = String::from_utf8_lossy(&proc_output.stderr).to_string(); + + let priv_key = + SigningKey::from_slice(&hex::decode(proc_output_str.trim()).unwrap()).unwrap(); + let pub_key = *priv_key.verifying_key(); + + KeyPair { priv_key, pub_key } + } +} diff --git a/rust/utils/run-locally/src/cosmos/crypto.rs b/rust/utils/run-locally/src/cosmos/crypto.rs new file mode 100644 index 0000000000..e4520cbcbf --- /dev/null +++ b/rust/utils/run-locally/src/cosmos/crypto.rs @@ -0,0 +1,47 @@ +// TODO: this file can be removed if `CosmosAddress` can be imported from `hyperlane-cosmos`. +// However, adding a hyperlane-cosmos dep creates a dep cycle. +// Look into how this can be fixed. + +use k256::ecdsa::{SigningKey, VerifyingKey}; +use ripemd::Ripemd160; +use sha2::{Digest, Sha256}; + +pub fn sha256_digest(bz: impl AsRef<[u8]>) -> [u8; 32] { + let mut hasher = Sha256::new(); + + hasher.update(bz); + + hasher.finalize().as_slice().try_into().unwrap() +} + +pub fn ripemd160_digest(bz: impl AsRef<[u8]>) -> [u8; 20] { + let mut hasher = Ripemd160::new(); + + hasher.update(bz); + + hasher.finalize().as_slice().try_into().unwrap() +} + +pub fn pub_to_addr(pub_key: &[u8], prefix: &str) -> String { + let sha_hash = sha256_digest(pub_key); + let rip_hash = ripemd160_digest(sha_hash); + + let addr = hpl_interface::types::bech32_encode(prefix, &rip_hash).unwrap(); + + addr.to_string() +} + +pub struct KeyPair { + pub priv_key: SigningKey, + pub pub_key: VerifyingKey, +} + +impl KeyPair { + pub fn pub_key_to_binary(&self) -> Vec { + self.pub_key.to_encoded_point(true).as_bytes().to_vec() + } + + pub fn addr(&self, hrp: &str) -> String { + pub_to_addr(&self.pub_key_to_binary(), hrp) + } +} diff --git a/rust/utils/run-locally/src/cosmos/deploy.rs b/rust/utils/run-locally/src/cosmos/deploy.rs new file mode 100644 index 0000000000..859f9f05dd --- /dev/null +++ b/rust/utils/run-locally/src/cosmos/deploy.rs @@ -0,0 +1,199 @@ +use cosmwasm_schema::cw_serde; +use hpl_interface::{core, hook, igp, ism}; +use macro_rules_attribute::apply; + +use crate::utils::as_task; + +use super::{ + cli::{OsmosisCLI, OsmosisEndpoint}, + types::{Codes, Deployments}, +}; + +#[cw_serde] +pub struct IsmMultisigInstantiateMsg { + pub owner: String, +} + +#[cw_serde] +pub struct TestMockMsgReceiverInstantiateMsg { + pub hrp: String, +} + +#[cw_serde] +pub struct IGPOracleInstantiateMsg { + pub owner: String, +} + +#[cw_serde] +pub struct EmptyMsg {} + +const PREFIX: &str = "osmo"; + +#[apply(as_task)] +pub fn deploy_cw_hyperlane( + cli: OsmosisCLI, + endpoint: OsmosisEndpoint, + deployer: String, + codes: Codes, + domain: u32, +) -> Deployments { + let deployer_addr = &cli.get_addr(&deployer); + + let mailbox = cli.wasm_init( + &endpoint, + &deployer, + Some(deployer_addr), + codes.hpl_mailbox, + core::mailbox::InstantiateMsg { + owner: deployer_addr.to_string(), + hrp: PREFIX.to_string(), + domain, + }, + "hpl_mailbox", + ); + + // deploy igp set + #[cw_serde] + pub struct GasOracleInitMsg { + pub hrp: String, + pub owner: String, + pub gas_token: String, + pub beneficiary: String, + pub default_gas_usage: String, + } + + let igp = cli.wasm_init( + &endpoint, + &deployer, + Some(deployer_addr), + codes.hpl_igp, + GasOracleInitMsg { + hrp: PREFIX.to_string(), + owner: deployer_addr.clone(), + gas_token: "uosmo".to_string(), + beneficiary: deployer_addr.clone(), + default_gas_usage: "25000".to_string(), + }, + "hpl_igp", + ); + + let igp_oracle = cli.wasm_init( + &endpoint, + &deployer, + Some(deployer_addr), + codes.hpl_igp_oracle, + igp::oracle::InstantiateMsg { + owner: deployer_addr.clone(), + }, + "hpl_igp_oracle", + ); + + // deploy ism - routing ism with empty routes + let ism_routing = cli.wasm_init( + &endpoint, + &deployer, + Some(deployer_addr), + codes.hpl_ism_routing, + ism::routing::InstantiateMsg { + owner: deployer_addr.clone(), + isms: vec![], + }, + "hpl_ism_routing", + ); + + // deploy ism - multisig ism with no enrolled validators + let ism_multisig = cli.wasm_init( + &endpoint, + &deployer, + Some(deployer_addr), + codes.hpl_ism_multisig, + IsmMultisigInstantiateMsg { + owner: deployer_addr.clone(), + }, + "hpl_ism_multisig", + ); + + // deploy merkle hook + let hook_merkle = cli.wasm_init( + &endpoint, + &deployer, + Some(deployer_addr), + codes.hpl_hook_merkle, + hook::merkle::InstantiateMsg { + owner: deployer_addr.clone(), + mailbox: mailbox.to_string(), + }, + "hpl_hook_merkle", + ); + + // deploy routing hook + let hook_routing = cli.wasm_init( + &endpoint, + &deployer, + Some(deployer_addr), + codes.hpl_hook_routing, + hook::routing::InstantiateMsg { + owner: deployer_addr.clone(), + }, + "hpl_hook_routing", + ); + + // deploy va + let va = cli.wasm_init( + &endpoint, + &deployer, + Some(deployer_addr), + codes.hpl_validator_announce, + core::va::InstantiateMsg { + hrp: PREFIX.to_string(), + mailbox: mailbox.to_string(), + }, + "hpl_validator_announce", + ); + + // ---------- mock area ----------- + // deploy mock receiver + let mock_receiver = cli.wasm_init( + &endpoint, + &deployer, + Some(deployer_addr), + codes.hpl_test_mock_msg_receiver, + TestMockMsgReceiverInstantiateMsg { + hrp: PREFIX.to_string(), + }, + "hpl_test_mock_msg_receiver", + ); + + // deploy mock hook + let mock_hook = cli.wasm_init( + &endpoint, + &deployer, + Some(deployer_addr), + codes.hpl_test_mock_hook, + EmptyMsg {}, + "hpl_test_mock_hook", + ); + + let mock_ism = cli.wasm_init( + &endpoint, + &deployer, + Some(deployer_addr), + codes.hpl_test_mock_ism, + EmptyMsg {}, + "hpl_test_mock_ism", + ); + + Deployments { + hook_merkle, + hook_routing, + igp, + igp_oracle, + ism_routing, + ism_multisig, + mailbox, + mock_receiver, + mock_hook, + mock_ism, + va, + } +} diff --git a/rust/utils/run-locally/src/cosmos/link.rs b/rust/utils/run-locally/src/cosmos/link.rs new file mode 100644 index 0000000000..1e65aa4b25 --- /dev/null +++ b/rust/utils/run-locally/src/cosmos/link.rs @@ -0,0 +1,270 @@ +use std::path::Path; + +use cosmwasm_schema::cw_serde; +use hpl_interface::{ + core, + ism::{self}, +}; + +use super::{cli::OsmosisCLI, crypto::KeyPair, CosmosNetwork}; + +#[cw_serde] +pub struct GeneralIsmMessage { + pub ism: T, +} + +#[cw_serde] +pub struct GeneralRouterMessage { + pub router: T, +} + +#[cw_serde] +pub struct GeneralHookMessage { + pub hook: T, +} + +#[cw_serde] +pub struct MockRouterMsg { + pub set_route: MockRouterMsgInner, +} + +#[cw_serde] +pub struct MockRouterMsgInner { + pub set: MockDomainRouteSet, +} + +#[cw_serde] +pub struct MockDomainRouteSet { + pub domain: u32, + pub route: String, +} + +#[cw_serde] +pub struct RemoteGasDataConfig { + pub remote_domain: u32, + pub token_exchange_rate: String, + pub gas_price: String, +} + +#[cw_serde] +pub struct RemoteGasDataConfigExecute { + pub set_remote_gas_data_configs: RemoteGasDataConfigExecuteInner, +} + +#[cw_serde] +pub struct RemoteGasDataConfigExecuteInner { + pub configs: Vec, +} + +#[cw_serde] +pub struct MockHookQueryMsg { + quote_dispatch: MockQuoteDispatch, +} + +#[cw_serde] +pub struct MockQuoteDispatch { + pub metadata: String, + pub message: String, +} + +#[cw_serde] +pub struct GeneralIsmValidatorMessage { + pub enroll_validator: EnrollValidatorMsg, +} + +#[cw_serde] +pub struct EnrollValidatorMsg { + pub set: EnrollValidatorSet, +} + +#[cw_serde] +pub struct EnrollValidatorSet { + pub domain: u32, + pub validator: String, +} + +fn link_network( + cli: &OsmosisCLI, + network: &CosmosNetwork, + hrp: &str, + linker: &str, + validator: &KeyPair, + target_domain: u32, +) { + let validator_addr = validator.addr(hrp); + + let dest_domain = if network.domain == 26657 { + 26658 + } else { + 26657 + }; + + // hook routing + + // link src chain + let public_key = validator.priv_key.verifying_key().to_encoded_point(false); + let public_key = public_key.as_bytes(); + + let hash = hpl_interface::types::keccak256_hash(&public_key[1..]); + + let mut bytes = [0u8; 20]; + bytes.copy_from_slice(&hash.as_slice()[12..]); + + cli.wasm_execute( + &network.launch_resp.endpoint, + linker, + &network.deployments.ism_multisig, + GeneralIsmValidatorMessage { + enroll_validator: EnrollValidatorMsg { + set: EnrollValidatorSet { + domain: target_domain, + validator: hex::encode(bytes).to_string(), + }, + }, + }, + vec![], + ); + + cli.wasm_execute( + &network.launch_resp.endpoint, + linker, + &network.deployments.ism_multisig, + ism::multisig::ExecuteMsg::SetThreshold { + set: ism::multisig::ThresholdSet { + domain: target_domain, + threshold: 1, + }, + }, + vec![], + ); + + cli.wasm_execute( + &network.launch_resp.endpoint, + linker, + &network.deployments.hook_routing, + GeneralRouterMessage { + router: MockRouterMsg { + set_route: MockRouterMsgInner { + set: MockDomainRouteSet { + domain: target_domain, + route: network.deployments.hook_merkle.clone(), + }, + }, + }, + }, + vec![], + ); + + cli.wasm_execute( + &network.launch_resp.endpoint, + linker, + &network.deployments.ism_routing, + ism::routing::ExecuteMsg::Set { + ism: ism::routing::IsmSet { + domain: target_domain, + address: network.deployments.ism_multisig.clone(), + }, + }, + vec![], + ); + + cli.wasm_execute( + &network.launch_resp.endpoint, + linker, + &network.deployments.mailbox, + core::mailbox::ExecuteMsg::SetDefaultHook { + hook: network.deployments.hook_routing.clone(), + }, + vec![], + ); + + cli.wasm_execute( + &network.launch_resp.endpoint, + linker, + &network.deployments.igp_oracle, + RemoteGasDataConfigExecute { + set_remote_gas_data_configs: RemoteGasDataConfigExecuteInner { + configs: vec![RemoteGasDataConfig { + remote_domain: dest_domain, + token_exchange_rate: "10000".to_string(), + gas_price: "1000000000".to_string(), + }], + }, + }, + vec![], + ); + + cli.wasm_execute( + &network.launch_resp.endpoint, + linker, + &network.deployments.igp, + GeneralRouterMessage { + router: MockRouterMsg { + set_route: MockRouterMsgInner { + set: MockDomainRouteSet { + domain: target_domain, + route: network.deployments.igp_oracle.clone(), + }, + }, + }, + }, + vec![], + ); + + cli.wasm_execute( + &network.launch_resp.endpoint, + linker, + &network.deployments.mailbox, + core::mailbox::ExecuteMsg::SetRequiredHook { + hook: network.deployments.igp.clone(), + }, + vec![], + ); + + cli.wasm_execute( + &network.launch_resp.endpoint, + linker, + &network.deployments.mailbox, + core::mailbox::ExecuteMsg::SetDefaultIsm { + ism: network.deployments.ism_routing.clone(), + }, + vec![], + ); + + cli.bank_send( + &network.launch_resp.endpoint, + linker, + &validator_addr, + "osmo1l83956lgpak5sun7ggupls7rk7p5cr95499jdf", + "10000000uosmo", + ); + + // TODO + // cli.wasm_execute( + // &network.launch_resp.endpoint, + // linker, + // &network.deployments.va, + // va::ExecuteMsg::Announce { + // validator: (), + // storage_location: (), + // signature: (), + // }, + // vec![], + // ); +} + +pub fn link_networks( + bin: &Path, + linker: &str, + validator: &str, + src: &CosmosNetwork, + dst: &CosmosNetwork, +) { + let src_cli = src.launch_resp.cli(bin); + let dst_cli = dst.launch_resp.cli(bin); + + let keypair = src_cli.get_keypair(validator); + + link_network(&src_cli, src, "osmo", linker, &keypair, dst.domain); + link_network(&dst_cli, dst, "osmo", linker, &keypair, src.domain); +} diff --git a/rust/utils/run-locally/src/cosmos/mod.rs b/rust/utils/run-locally/src/cosmos/mod.rs new file mode 100644 index 0000000000..1ecb26dc0c --- /dev/null +++ b/rust/utils/run-locally/src/cosmos/mod.rs @@ -0,0 +1,587 @@ +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; +use std::thread::sleep; +use std::time::{Duration, Instant}; +use std::{env, fs}; + +use cosmwasm_schema::cw_serde; +use hpl_interface::types::bech32_decode; +use macro_rules_attribute::apply; +use tempfile::tempdir; + +mod cli; +mod crypto; +mod deploy; +mod link; +mod rpc; +mod source; +mod types; +mod utils; + +use rpc::*; +use types::*; +use utils::*; + +use crate::cosmos::link::link_networks; +use crate::logging::log; +use crate::program::Program; +use crate::utils::{as_task, concat_path, stop_child, AgentHandles, TaskHandle}; +use crate::AGENT_BIN_PATH; +use cli::{OsmosisCLI, OsmosisEndpoint}; + +use self::deploy::deploy_cw_hyperlane; +use self::source::{CLISource, CodeSource}; + +const OSMOSIS_CLI_GIT: &str = "https://github.com/osmosis-labs/osmosis"; +const OSMOSIS_CLI_VERSION: &str = "19.0.0"; + +const KEY_HPL_VALIDATOR: (&str,&str) = ("hpl-validator", "guard evolve region sentence danger sort despair eye deputy brave trim actor left recipe debate document upgrade sustain bus cage afford half demand pigeon"); +const KEY_HPL_RELAYER: (&str,&str) = ("hpl-relayer", "moral item damp melt gloom vendor notice head assume balance doctor retire fashion trim find biology saddle undo switch fault cattle toast drip empty"); + +const KEY_VALIDATOR: (&str,&str) = ("validator", "legend auto stand worry powder idle recall there wet ancient universe badge ability blame hidden body steak april boost thrive room piece city type"); +const KEY_ACCOUNTS1: (&str,&str) = ("account1", "stomach employ hidden risk fork parent dream noodle inside banner stable private grain nothing absent brave metal math hybrid amused move affair move muffin"); +const KEY_ACCOUNTS2: (&str,&str) = ("account2", "say merry worry steak hedgehog sing spike fold empower pluck feel grass omit finish biology traffic dog sea ozone hint region service one gown"); +const KEY_ACCOUNTS3: (&str,&str) = ("account3", "maple often cargo polar eager jaguar eight inflict once nest nice swamp weasel address swift physical valid culture cheese trumpet find dinosaur curve tray"); + +fn default_keys<'a>() -> [(&'a str, &'a str); 6] { + [ + KEY_HPL_VALIDATOR, + KEY_HPL_RELAYER, + KEY_VALIDATOR, + KEY_ACCOUNTS1, + KEY_ACCOUNTS2, + KEY_ACCOUNTS3, + ] +} + +const CW_HYPERLANE_GIT: &str = "https://github.com/many-things/cw-hyperlane"; +const CW_HYPERLANE_VERSION: &str = "0.0.6-rc6"; + +fn make_target() -> String { + let os = if cfg!(target_os = "linux") { + "linux" + } else if cfg!(target_os = "macos") { + "darwin" + } else { + panic!("Current os is not supported by Osmosis") + }; + + let arch = if cfg!(target_arch = "aarch64") { + "arm64" + } else { + "amd64" + }; + + format!("{}-{}", os, arch) +} + +#[cw_serde] +pub struct MockDispatch { + pub dispatch: MockDispatchInner, +} + +#[cw_serde] +pub struct MockDispatchInner { + pub dest_domain: u32, + pub recipient_addr: String, + pub msg_body: String, + pub hook: Option, + pub metadata: String, +} + +pub fn install_codes(dir: Option, local: bool) -> BTreeMap { + let dir_path = match dir { + Some(path) => path, + None => tempdir().unwrap().into_path(), + }; + + if !local { + let dir_path_str = dir_path.to_str().unwrap(); + + let release_name = format!("cw-hyperlane-v{CW_HYPERLANE_VERSION}"); + let release_comp = format!("{release_name}.zip"); + + log!("Downloading cw-hyperlane v{}", CW_HYPERLANE_VERSION); + let uri = + format!("{CW_HYPERLANE_GIT}/releases/download/v{CW_HYPERLANE_VERSION}/{release_comp}"); + download(&release_comp, &uri, dir_path_str); + + log!("Uncompressing cw-hyperlane release"); + unzip(&release_comp, dir_path_str); + } + + log!("Installing cw-hyperlane in Path: {:?}", dir_path); + + // make contract_name => path map + fs::read_dir(dir_path) + .unwrap() + .map(|v| { + let entry = v.unwrap(); + (entry.file_name().into_string().unwrap(), entry.path()) + }) + .filter(|(filename, _)| filename.ends_with(".wasm")) + .map(|v| (v.0.replace(".wasm", ""), v.1)) + .collect() +} + +#[allow(dead_code)] +pub fn install_cosmos( + cli_dir: Option, + cli_src: Option, + codes_dir: Option, + _codes_src: Option, +) -> (PathBuf, BTreeMap) { + let osmosisd = cli_src + .unwrap_or(CLISource::Remote { + url: OSMOSIS_CLI_GIT.to_string(), + version: OSMOSIS_CLI_VERSION.to_string(), + }) + .install(cli_dir); + let codes = install_codes(codes_dir, false); + + (osmosisd, codes) +} + +#[derive(Clone)] +pub struct CosmosConfig { + pub cli_path: PathBuf, + pub home_path: Option, + + pub codes: BTreeMap, + + pub node_addr_base: String, + pub node_port_base: u32, + + pub moniker: String, + pub chain_id: String, +} + +pub struct CosmosResp { + pub node: AgentHandles, + pub endpoint: OsmosisEndpoint, + pub codes: Codes, + pub home_path: PathBuf, +} + +impl CosmosResp { + pub fn cli(&self, bin: &Path) -> OsmosisCLI { + OsmosisCLI::new(bin.to_path_buf(), self.home_path.to_str().unwrap()) + } +} + +pub struct CosmosNetwork { + pub launch_resp: CosmosResp, + pub deployments: Deployments, + pub chain_id: String, + pub domain: u32, +} + +impl Drop for CosmosNetwork { + fn drop(&mut self) { + stop_child(&mut self.launch_resp.node.1); + } +} + +impl From<(CosmosResp, Deployments, String, u32)> for CosmosNetwork { + fn from(v: (CosmosResp, Deployments, String, u32)) -> Self { + Self { + launch_resp: v.0, + deployments: v.1, + chain_id: v.2, + domain: v.3, + } + } +} + +pub struct CosmosHyperlaneStack { + pub validators: Vec, + pub relayer: AgentHandles, +} + +impl Drop for CosmosHyperlaneStack { + fn drop(&mut self) { + for v in &mut self.validators { + stop_child(&mut v.1); + } + stop_child(&mut self.relayer.1); + } +} + +#[apply(as_task)] +fn launch_cosmos_node(config: CosmosConfig) -> CosmosResp { + let home_path = match config.home_path { + Some(v) => v, + None => tempdir().unwrap().into_path(), + }; + let cli = OsmosisCLI::new(config.cli_path, home_path.to_str().unwrap()); + + cli.init(&config.moniker, &config.chain_id); + + let (node, endpoint) = cli.start(config.node_addr_base, config.node_port_base); + let codes = cli.store_codes(&endpoint, "validator", config.codes); + + CosmosResp { + node, + endpoint, + codes, + home_path, + } +} + +#[apply(as_task)] +fn launch_cosmos_validator( + agent_config: AgentConfig, + agent_config_path: PathBuf, + debug: bool, +) -> AgentHandles { + let validator_bin = concat_path(format!("../../{AGENT_BIN_PATH}"), "validator"); + let validator_base = tempdir().expect("Failed to create a temp dir").into_path(); + let validator_base_db = concat_path(&validator_base, "db"); + + fs::create_dir_all(&validator_base_db).unwrap(); + println!("Validator DB: {:?}", validator_base_db); + + let checkpoint_path = concat_path(&validator_base, "checkpoint"); + let signature_path = concat_path(&validator_base, "signature"); + + let validator = Program::default() + .bin(validator_bin) + .working_dir("../../") + .env("CONFIG_FILES", agent_config_path.to_str().unwrap()) + .env( + "MY_VALIDATOR_SIGNATURE_DIRECTORY", + signature_path.to_str().unwrap(), + ) + .env("RUST_BACKTRACE", "1") + .hyp_env("CHECKPOINTSYNCER_PATH", checkpoint_path.to_str().unwrap()) + .hyp_env("CHECKPOINTSYNCER_TYPE", "localStorage") + .hyp_env("ORIGINCHAINNAME", agent_config.name) + .hyp_env("REORGPERIOD", "100") + .hyp_env("DB", validator_base_db.to_str().unwrap()) + .hyp_env("METRICSPORT", agent_config.domain_id.to_string()) + .hyp_env("VALIDATOR_SIGNER_TYPE", agent_config.signer.typ) + .hyp_env("VALIDATOR_KEY", agent_config.signer.key.clone()) + .hyp_env("VALIDATOR_PREFIX", "osmo") + .hyp_env("SIGNER_SIGNER_TYPE", "hexKey") + .hyp_env("SIGNER_KEY", agent_config.signer.key) + .hyp_env("TRACING_LEVEL", if debug { "debug" } else { "info" }) + .spawn("VAL"); + + validator +} + +#[apply(as_task)] +fn launch_cosmos_relayer( + agent_config_path: PathBuf, + relay_chains: Vec, + debug: bool, +) -> AgentHandles { + let relayer_bin = concat_path(format!("../../{AGENT_BIN_PATH}"), "relayer"); + let relayer_base = tempdir().unwrap(); + + let relayer = Program::default() + .bin(relayer_bin) + .working_dir("../../") + .env("CONFIG_FILES", agent_config_path.to_str().unwrap()) + .env("RUST_BACKTRACE", "1") + .hyp_env("RELAYCHAINS", relay_chains.join(",")) + .hyp_env("REORGPERIOD", "100") + .hyp_env("DB", relayer_base.as_ref().to_str().unwrap()) + .hyp_env("ALLOWLOCALCHECKPOINTSYNCERS", "true") + .hyp_env("TRACING_LEVEL", if debug { "debug" } else { "info" }) + .hyp_env("GASPAYMENTENFORCEMENT", "[{\"type\": \"none\"}]") + .hyp_env("METRICSPORT", 9093.to_string()) + .spawn("RLY"); + + relayer +} + +const ENV_CLI_PATH_KEY: &str = "E2E_OSMOSIS_CLI_PATH"; +const ENV_CW_HYPERLANE_PATH_KEY: &str = "E2E_CW_HYPERLANE_PATH"; + +#[allow(dead_code)] +fn run_locally() { + const TIMEOUT_SECS: u64 = 60 * 10; + let debug = false; + + log!("Building rust..."); + Program::new("cargo") + .cmd("build") + .working_dir("../../") + .arg("features", "test-utils") + .arg("bin", "relayer") + .arg("bin", "validator") + .arg("bin", "scraper") + .arg("bin", "init-db") + .filter_logs(|l| !l.contains("workspace-inheritance")) + .run() + .join(); + + let cli_src = Some( + env::var(ENV_CLI_PATH_KEY) + .as_ref() + .map(|v| CLISource::local(v)) + .unwrap_or_default(), + ); + + let code_src = Some( + env::var(ENV_CW_HYPERLANE_PATH_KEY) + .as_ref() + .map(|v| CodeSource::local(v)) + .unwrap_or_default(), + ); + + let (osmosisd, codes) = install_cosmos(None, cli_src, None, code_src); + let addr_base = "tcp://0.0.0.0"; + let default_config = CosmosConfig { + cli_path: osmosisd.clone(), + home_path: None, + + codes, + + node_addr_base: addr_base.to_string(), + node_port_base: 26657, + + moniker: "localnet".to_string(), + chain_id: "local-node".to_string(), + }; + + let port_start = 26600u32; + let domain_start = 26657u32; + let node_count = 2; + + let nodes = (0..node_count) + .map(|i| { + ( + launch_cosmos_node(CosmosConfig { + node_port_base: port_start + (i * 10), + chain_id: format!("cosmos-test-{}", i + 26657), + ..default_config.clone() + }), + format!("cosmos-test-{}", i + 26657), + domain_start + i, + ) + }) + .collect::>(); + + let deployer = "validator"; + let linker = "validator"; + let validator = "hpl-validator"; + let _relayer = "hpl-relayer"; + + let nodes = nodes + .into_iter() + .map(|v| (v.0.join(), v.1, v.2)) + .map(|(launch_resp, chain_id, domain)| { + let deployments = deploy_cw_hyperlane( + launch_resp.cli(&osmosisd), + launch_resp.endpoint.clone(), + deployer.to_string(), + launch_resp.codes.clone(), + domain, + ); + + (launch_resp, deployments, chain_id, domain) + }) + .collect::>(); + + // nodes with base deployments + let nodes = nodes + .into_iter() + .map(|v| (v.0, v.1.join(), v.2, v.3)) + .map(|v| v.into()) + .collect::>(); + + for (i, node) in nodes.iter().enumerate() { + let targets = &nodes[(i + 1)..]; + + if !targets.is_empty() { + println!( + "LINKING NODES: {} -> {:?}", + node.domain, + targets.iter().map(|v| v.domain).collect::>() + ); + } + + for target in targets { + link_networks(&osmosisd, linker, validator, node, target); + } + } + + // for debug + println!( + "{}", + serde_json::to_string( + &nodes + .iter() + .map(|v| (v.domain, v.deployments.clone())) + .collect::>() + ) + .unwrap() + ); + + let config_dir = tempdir().unwrap(); + + // export agent config + let agent_config_out = AgentConfigOut { + chains: nodes + .iter() + .map(|v| { + ( + format!("cosmostest{}", v.domain), + AgentConfig::new(osmosisd.clone(), validator, v), + ) + }) + .collect::>(), + }; + + let agent_config_path = concat_path(&config_dir, "config.json"); + fs::write( + &agent_config_path, + serde_json::to_string_pretty(&agent_config_out).unwrap(), + ) + .unwrap(); + + let hpl_val = agent_config_out + .chains + .clone() + .into_values() + .map(|agent_config| launch_cosmos_validator(agent_config, agent_config_path.clone(), debug)) + .collect::>(); + let hpl_rly = launch_cosmos_relayer( + agent_config_path, + agent_config_out.chains.into_keys().collect::>(), + debug, + ); + + // dispatch messages + let mut dispatched_messages = 0; + + for node in nodes.iter() { + let targets = nodes + .iter() + .filter(|v| v.domain != node.domain) + .collect::>(); + + if !targets.is_empty() { + println!( + "DISPATCHING MAILBOX: {} -> {:?}", + node.domain, + targets.iter().map(|v| v.domain).collect::>() + ); + } + + for target in targets { + dispatched_messages += 1; + let cli = OsmosisCLI::new( + osmosisd.clone(), + node.launch_resp.home_path.to_str().unwrap(), + ); + + let msg_body: &[u8; 5] = b"hello"; + + cli.wasm_execute( + &node.launch_resp.endpoint, + linker, + &node.deployments.mailbox, + MockDispatch { + dispatch: MockDispatchInner { + dest_domain: target.domain, + recipient_addr: hex::encode( + bech32_decode(&target.deployments.mock_receiver).unwrap(), + ), + msg_body: hex::encode(msg_body), + hook: None, + metadata: "".to_string(), + }, + }, + vec![Coin { + denom: "uosmo".to_string(), + amount: 25_000_000.to_string(), + }], + ); + } + } + + let _stack = CosmosHyperlaneStack { + validators: hpl_val.into_iter().map(|v| v.join()).collect(), + relayer: hpl_rly.join(), + }; + + // Mostly copy-pasta from `rust/utils/run-locally/src/main.rs` + // TODO: refactor to share code + let loop_start = Instant::now(); + // give things a chance to fully start. + sleep(Duration::from_secs(5)); + let mut failure_occurred = false; + loop { + // look for the end condition. + if termination_invariants_met(dispatched_messages).unwrap_or(false) { + // end condition reached successfully + break; + } else if (Instant::now() - loop_start).as_secs() > TIMEOUT_SECS { + // we ran out of time + log!("timeout reached before message submission was confirmed"); + failure_occurred = true; + break; + } + + sleep(Duration::from_secs(5)); + } + + if failure_occurred { + panic!("E2E tests failed"); + } else { + log!("E2E tests passed"); + } +} + +fn termination_invariants_met(_messages_expected: u32) -> eyre::Result { + Ok(true) + // TODO: uncomment once CI passes consistently on Ubuntu + // let gas_payments_scraped = fetch_metric( + // "9093", + // "hyperlane_contract_sync_stored_events", + // &hashmap! {"data_type" => "gas_payment"}, + // )? + // .iter() + // .sum::(); + // let expected_gas_payments = messages_expected; + // if gas_payments_scraped != expected_gas_payments { + // log!( + // "Scraper has scraped {} gas payments, expected {}", + // gas_payments_scraped, + // expected_gas_payments + // ); + // return Ok(false); + // } + + // let delivered_messages_scraped = fetch_metric( + // "9093", + // "hyperlane_operations_processed_count", + // &hashmap! {"phase" => "confirmed"}, + // )? + // .iter() + // .sum::(); + // if delivered_messages_scraped != messages_expected { + // log!( + // "Relayer confirmed {} submitted messages, expected {}", + // delivered_messages_scraped, + // messages_expected + // ); + // return Ok(false); + // } + + // log!("Termination invariants have been meet"); + // Ok(true) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_run() { + run_locally() + } +} diff --git a/rust/utils/run-locally/src/cosmos/rpc.rs b/rust/utils/run-locally/src/cosmos/rpc.rs new file mode 100644 index 0000000000..50fa5e852a --- /dev/null +++ b/rust/utils/run-locally/src/cosmos/rpc.rs @@ -0,0 +1,42 @@ +use std::{thread::sleep, time::Duration}; + +use ureq::get; + +const MAX_REQUEST_COUNT: i32 = 30; + +#[derive(serde::Serialize, serde::Deserialize)] +struct JsonRpcResp { + pub jsonrpc: String, + pub id: i32, + pub result: serde_json::Value, +} + +pub fn wait_for_node(rpc_addr: &str) { + let mut count = 0; + loop { + if count > MAX_REQUEST_COUNT { + panic!("failed to start node"); + } + + let req_url = format!("{}/status", rpc_addr.replace("tcp", "http")); + if let Ok(resp) = get(&req_url).call() { + if resp.status() == 200 { + let rpc_resp: JsonRpcResp = + serde_json::from_str(&resp.into_string().unwrap()).unwrap(); + + let rpc_resp = rpc_resp.result.as_object().unwrap(); + let rpc_resp = rpc_resp["sync_info"].as_object().unwrap(); + + let latest_block_height = rpc_resp["latest_block_height"].as_str().unwrap(); + let latest_block_height = latest_block_height.parse::().unwrap(); + + if latest_block_height > 0 { + break; + } + } + } + + sleep(Duration::from_secs(1)); + count += 1; + } +} diff --git a/rust/utils/run-locally/src/cosmos/source.rs b/rust/utils/run-locally/src/cosmos/source.rs new file mode 100644 index 0000000000..655e3b48ac --- /dev/null +++ b/rust/utils/run-locally/src/cosmos/source.rs @@ -0,0 +1,157 @@ +use std::{collections::BTreeMap, fs, path::PathBuf}; + +use tempfile::tempdir; + +use crate::{ + cosmos::{ + make_target, + utils::{download, untar}, + }, + logging::log, + utils::concat_path, +}; + +use super::{CW_HYPERLANE_GIT, CW_HYPERLANE_VERSION, OSMOSIS_CLI_GIT, OSMOSIS_CLI_VERSION}; + +pub enum CodeSource { + Local { path: String }, + Remote { url: String, version: String }, +} + +impl Default for CodeSource { + fn default() -> Self { + Self::remote(CW_HYPERLANE_GIT, CW_HYPERLANE_VERSION) + } +} + +impl CodeSource { + pub fn local(path: &str) -> Self { + Self::Local { + path: path.to_string(), + } + } + + pub fn remote(url: &str, version: &str) -> Self { + Self::Remote { + url: url.to_string(), + version: version.to_string(), + } + } +} + +impl CodeSource { + fn install_local(src: String) -> BTreeMap { + // make contract_name => path map + fs::read_dir(src) + .unwrap() + .map(|v| { + let entry = v.unwrap(); + (entry.file_name().into_string().unwrap(), entry.path()) + }) + .filter(|(filename, _)| filename.ends_with(".wasm")) + .map(|v| (v.0.replace(".wasm", ""), v.1)) + .collect() + } + + fn install_remote( + dir: Option, + git: String, + version: String, + ) -> BTreeMap { + let dir_path = match dir { + Some(path) => path, + None => tempdir().unwrap().into_path(), + }; + let dir_path = dir_path.to_str().unwrap(); + + let release_name = format!("cw-hyperlane-v{version}"); + let release_comp = format!("{release_name}.tar.gz"); + + log!("Downloading cw-hyperlane v{}", version); + let uri = format!("{git}/releases/download/v{version}/{release_comp}"); + download(&release_comp, &uri, dir_path); + + log!("Uncompressing cw-hyperlane release"); + untar(&release_comp, dir_path); + + // make contract_name => path map + fs::read_dir(concat_path(dir_path, release_name)) + .unwrap() + .map(|v| { + let entry = v.unwrap(); + (entry.file_name().into_string().unwrap(), entry.path()) + }) + .filter(|(filename, _)| filename.ends_with(".wasm")) + .map(|v| (v.0.replace(".wasm", ""), v.1)) + .collect() + } + + #[allow(dead_code)] + pub fn install(self, dir: Option) -> BTreeMap { + match self { + CodeSource::Local { path } => Self::install_local(path), + CodeSource::Remote { url, version } => Self::install_remote(dir, url, version), + } + } +} + +pub enum CLISource { + Local { path: String }, + Remote { url: String, version: String }, +} + +impl Default for CLISource { + fn default() -> Self { + if make_target().starts_with("darwin") { + Self::remote("https://github.com/hashableric/osmosis", "19.0.0-mnts") + } else { + Self::remote(OSMOSIS_CLI_GIT, OSMOSIS_CLI_VERSION) + } + } +} + +impl CLISource { + pub fn local(path: &str) -> Self { + Self::Local { + path: path.to_string(), + } + } + + pub fn remote(url: &str, version: &str) -> Self { + Self::Remote { + url: url.to_string(), + version: version.to_string(), + } + } +} + +impl CLISource { + fn install_remote(dir: Option, git: String, version: String) -> PathBuf { + let target = make_target(); + + let dir_path = match dir { + Some(path) => path, + None => tempdir().unwrap().into_path(), + }; + let dir_path = dir_path.to_str().unwrap(); + + let release_name = format!("osmosisd-{version}-{target}"); + let release_comp = format!("{release_name}.tar.gz"); + + log!("Downloading Osmosis CLI v{}", version); + let uri = format!("{git}/releases/download/v{version}/{release_comp}"); + download(&release_comp, &uri, dir_path); + + log!("Uncompressing Osmosis release"); + untar(&release_comp, dir_path); + + concat_path(dir_path, "osmosisd") + } + + pub fn install(self, dir: Option) -> PathBuf { + match self { + CLISource::Local { path } => path.into(), + CLISource::Remote { url, version } => Self::install_remote(dir, url, version), + } + } +} diff --git a/rust/utils/run-locally/src/cosmos/types.rs b/rust/utils/run-locally/src/cosmos/types.rs new file mode 100644 index 0000000000..8632687b15 --- /dev/null +++ b/rust/utils/run-locally/src/cosmos/types.rs @@ -0,0 +1,173 @@ +use std::{collections::BTreeMap, path::PathBuf}; + +use hpl_interface::types::bech32_decode; + +use super::{cli::OsmosisCLI, CosmosNetwork}; + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct TxEventAttr { + pub key: String, + pub value: String, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct TxEvent { + #[serde(rename = "type")] + pub typ: String, + pub attributes: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct TxLog { + pub msg_index: u32, + pub log: String, + pub events: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct TxResponse { + pub height: String, + pub txhash: String, + pub codespace: String, + pub code: u32, + pub data: String, + pub raw_log: String, + pub logs: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct Coin { + pub denom: String, + pub amount: String, +} + +#[derive(serde::Serialize, serde::Deserialize, Clone)] +pub struct Codes { + pub hpl_hook_merkle: u64, + pub hpl_hook_routing: u64, + pub hpl_igp: u64, + pub hpl_igp_oracle: u64, + pub hpl_ism_multisig: u64, + pub hpl_ism_routing: u64, + pub hpl_test_mock_ism: u64, + pub hpl_test_mock_hook: u64, + pub hpl_test_mock_msg_receiver: u64, + pub hpl_mailbox: u64, + pub hpl_validator_announce: u64, +} + +#[derive(serde::Serialize, serde::Deserialize, Clone)] +pub struct Deployments { + pub hook_merkle: String, + pub hook_routing: String, + pub igp: String, + pub igp_oracle: String, + pub ism_routing: String, + pub ism_multisig: String, + pub mailbox: String, + pub mock_receiver: String, + pub mock_hook: String, + pub mock_ism: String, + pub va: String, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct BalanceResponse { + pub balances: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct CliWasmQueryResponse { + pub data: T, +} + +#[derive(serde::Serialize, serde::Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct AgentConfigAddrs { + pub mailbox: String, + pub interchain_gas_paymaster: String, + pub validator_announce: String, +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct AgentConfigSigner { + #[serde(rename = "type")] + pub typ: String, + pub key: String, + pub prefix: String, +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct AgentConfigIndex { + pub from: u32, + pub chunk: u32, +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct AgentUrl { + pub http: String, +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct AgentConfig { + pub name: String, + pub domain_id: u32, + pub mailbox: String, + pub interchain_gas_paymaster: String, + pub validator_announce: String, + pub merkle_tree_hook: String, + pub protocol: String, + pub finality_blocks: u32, + pub chain_id: String, + pub rpc_urls: Vec, + pub grpc_url: String, + pub prefix: String, + pub signer: AgentConfigSigner, + pub index: AgentConfigIndex, +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct AgentConfigOut { + pub chains: BTreeMap, +} + +fn to_hex_addr(addr: &str) -> String { + format!("0x{}", hex::encode(bech32_decode(addr).unwrap())) +} + +impl AgentConfig { + pub fn new(bin: PathBuf, validator: &str, network: &CosmosNetwork) -> Self { + let cli = OsmosisCLI::new(bin, network.launch_resp.home_path.to_str().unwrap()); + let validator = cli.get_keypair(validator); + + AgentConfig { + name: format!("cosmostest{}", network.domain), + domain_id: network.domain, + mailbox: to_hex_addr(&network.deployments.mailbox), + interchain_gas_paymaster: to_hex_addr(&network.deployments.igp), + validator_announce: to_hex_addr(&network.deployments.va), + merkle_tree_hook: to_hex_addr(&network.deployments.hook_merkle), + protocol: "cosmos".to_string(), + finality_blocks: 1, + chain_id: format!("cosmos-test-{}", network.domain), + rpc_urls: vec![AgentUrl { + http: format!( + "http://{}", + network.launch_resp.endpoint.rpc_addr.replace("tcp://", "") + ), + }], + grpc_url: format!("http://{}", network.launch_resp.endpoint.grpc_addr), + prefix: "osmo".to_string(), + signer: AgentConfigSigner { + typ: "cosmosKey".to_string(), + key: format!("0x{}", hex::encode(validator.priv_key.to_bytes())), + prefix: "osmo".to_string(), + }, + index: AgentConfigIndex { + from: 1, + chunk: 100, + }, + } + } +} diff --git a/rust/utils/run-locally/src/cosmos/utils.rs b/rust/utils/run-locally/src/cosmos/utils.rs new file mode 100644 index 0000000000..07800b3c8f --- /dev/null +++ b/rust/utils/run-locally/src/cosmos/utils.rs @@ -0,0 +1,60 @@ +use std::{fs, path::PathBuf}; + +use toml_edit::Document; + +use crate::program::Program; +use crate::utils::TaskHandle; + +pub(crate) fn sed(from: &str, to: &str, file: &str) { + let mut program = Program::new("sed").raw_arg("-i"); + + if cfg!(target_os = "macos") { + program = program.cmd(""); + } + + program + .cmd(format!("s/{from}/{to}/g")) + .cmd(file) + .run() + .join() +} + +pub(crate) fn untar(output: &str, dir: &str) { + Program::new("tar") + .flag("extract") + .arg("file", output) + .working_dir(dir) + .run() + .join(); +} + +pub(crate) fn unzip(output: &str, dir: &str) { + Program::new("unzip") + .cmd(output) + .working_dir(dir) + .run() + .join(); +} + +pub(crate) fn download(output: &str, uri: &str, dir: &str) { + Program::new("curl") + .arg("output", output) + .flag("location") + .cmd(uri) + .flag("silent") + .working_dir(dir) + .run() + .join(); +} + +pub(crate) fn modify_toml(file: impl Into, modifier: Box) { + let path = file.into(); + let mut config = fs::read_to_string(&path) + .unwrap() + .parse::() + .unwrap(); + + modifier(&mut config); + + fs::write(path, config.to_string()).unwrap(); +} diff --git a/rust/utils/run-locally/src/main.rs b/rust/utils/run-locally/src/main.rs index 37afd5bc16..0ee63b02d4 100644 --- a/rust/utils/run-locally/src/main.rs +++ b/rust/utils/run-locally/src/main.rs @@ -35,6 +35,7 @@ use crate::{ }; mod config; +mod cosmos; mod ethereum; mod invariants; mod logging; @@ -166,7 +167,7 @@ fn main() -> ExitCode { // by setting this as a quorum provider we will cause nonce errors when delivering to test2 // because the message will be sent to the node 3 times. .hyp_env("CHAINS_TEST2_RPCCONSENSUSTYPE", "quorum") - .hyp_env("CHAINS_TEST3_RPCCONSENSUSTYPE", "http://127.0.0.1:8545") + .hyp_env("CHAINS_TEST3_CONNECTION_URL", "http://127.0.0.1:8545") .hyp_env("METRICSPORT", "9092") .hyp_env("DB", relayer_db.to_str().unwrap()) .hyp_env("CHAINS_TEST1_SIGNER_KEY", RELAYER_KEYS[0])